/*
The MIT License (MIT)

Copyright (c) 2015-2016 topameng(topameng@qq.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <string.h>
#if !defined __APPLE__
#include <malloc.h>
#endif
#include <stdbool.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "tolua.h"

#ifdef _WIN32
#include <windows.h>
#else
#include <time.h>
#include <sys/time.h>
#endif

int toluaflags = FLAG_INDEX_ERROR;
static int tag = 0;  
static int gettag = 0;
static int settag = 0;
static int vptr = 1;

/*---------------------------tolua extend functions--------------------------------*/
LUALIB_API void* tolua_tag()
{
	return &tag;
}

LUALIB_API void tolua_newudata(lua_State *L, int val)
{
	int* pointer = (int*)lua_newuserdata(L, sizeof(int));    
    lua_pushvalue(L, TOLUA_NOPEER);            
    lua_setfenv(L, -2);                        
	*pointer = val;
}

// 获取C#对象 在 ObjectTranslator.objects 中的下标。 lua端存的就是下标
LUALIB_API int tolua_rawnetobj(lua_State *L, int index)
{
    // 如果给定的可接受索引处的值是一个完整的userdata，则返回其块地址。如果值是轻userdata，则返回其指针。否则，返回NULL。
    int* udata = (int*)lua_touserdata(L, index);
    
    // userdata or lightuserdata
    if (udata != NULL) 
    {
        return *udata;
    }
    // lua table对象, peer表，调用setpeer(C++_obj, peer_table)。时绑定的
    // 这里的table就是这个peer_table
    else if (lua_istable(L, index))
    {
        // local obj = stack[index] 
        lua_pushvalue(L, index);            // .. -> obj      
        lua_pushlightuserdata(L, &vptr);    // .. -> obj -> &vptr
        lua_rawget(L, -2);                  // .. -> obj -> obj[&vptr]

        // 是userdata
        if (lua_isuserdata(L, -1))
        {
            lua_replace(L, index);
            udata = (int*)lua_touserdata(L, index);

            if (udata != NULL) 
            {
                return *udata;
            }
        }    
        else
        {
            // 恢复栈
            lua_pop(L, 1);
        }               
    }    

    // 获取到空了
	return -1;
}

LUALIB_API char* tolua_tocbuffer(const char *csBuffer, int sz)
{
	char* buffer = (char*)malloc(sz+1);
	memcpy(buffer, csBuffer, sz);	
	buffer[sz] = '\0';
	return buffer;
}

LUALIB_API void tolua_freebuffer(void* buffer)
{
  free(buffer);
}

LUALIB_API void tolua_getvec2(lua_State *L, int pos, float* x, float* y)
{
	lua_getref(L, LUA_RIDX_UNPACKVEC2);
	lua_pushvalue(L, pos);
	lua_call(L, 1, 2);
	*x = (float)lua_tonumber(L, -2);
	*y = (float)lua_tonumber(L, -1);
	lua_pop(L, 2);
}

LUALIB_API void tolua_getvec3(lua_State *L, int pos, float* x, float* y, float* z)
{
    // .. ->
	lua_getref(L, LUA_RIDX_UNPACKVEC3); // .. -> Vector3.Get
	lua_pushvalue(L, pos);              // .. -> Vector3.Get -> stack[pos](vector3 obj)
	lua_call(L, 1, 3);                  // .. -> Vector3.x -> Vector3.y -> Vector3.z
    *x = (float)lua_tonumber(L, -3);
    *y = (float)lua_tonumber(L, -2);
    *z = (float)lua_tonumber(L, -1);
    lua_pop(L, 3);                      // .. ->
}

LUALIB_API void tolua_getvec4(lua_State *L, int pos, float* x, float* y, float* z, float* w)
{
	lua_getref(L, LUA_RIDX_UNPACKVEC4);
	lua_pushvalue(L, pos);
	lua_call(L, 1, 4);
	*x = (float)lua_tonumber(L, -4);
	*y = (float)lua_tonumber(L, -3);
	*z = (float)lua_tonumber(L, -2);
	*w = (float)lua_tonumber(L, -1);
	lua_pop(L, 4);
}

LUALIB_API void tolua_getquat(lua_State *L, int pos, float* x, float* y, float* z, float* w)
{
	lua_getref(L, LUA_RIDX_UNPACKQUAT);
	lua_pushvalue(L, pos);
	lua_call(L, 1, 4);
	*x = (float)lua_tonumber(L, -4);
	*y = (float)lua_tonumber(L, -3);
	*z = (float)lua_tonumber(L, -2);
	*w = (float)lua_tonumber(L, -1);
	lua_pop(L, 4);
}

LUALIB_API void tolua_getclr(lua_State *L, int pos, float* r, float* g, float* b, float* a)
{
	lua_getref(L, LUA_RIDX_UNPACKCLR);
	lua_pushvalue(L, pos);
	lua_call(L, 1, 4);
	*r = (float)lua_tonumber(L, -4);
	*g = (float)lua_tonumber(L, -3);
	*b = (float)lua_tonumber(L, -2);
	*a = (float)lua_tonumber(L, -1);
	lua_pop(L, 4);
}

LUALIB_API int tolua_getlayermask(lua_State *L, int pos)
{
    if (lua_isnumber(L, pos))
    {
        return (int)lua_tointeger(L, pos);
    }

    lua_getref(L, LUA_RIDX_UNPACKLAYERMASK);
    lua_pushvalue(L, pos);
    lua_call(L, 1, 1);
    int mask = (int)lua_tointeger(L, -1);
    lua_pop(L, 1);
    return mask;
}

LUALIB_API void tolua_pushvec2(lua_State *L, float x, float y)
{
	lua_getref(L, LUA_RIDX_PACKVEC2);
	lua_pushnumber(L, x);
	lua_pushnumber(L, y);
	lua_call(L, 2, 1);
}

LUALIB_API void tolua_pushvec3(lua_State *L, float x, float y, float z)
{
	lua_getref(L, LUA_RIDX_PACKVEC3);
	lua_pushnumber(L, x);
	lua_pushnumber(L, y);
	lua_pushnumber(L, z);
	lua_call(L, 3, 1);
}

LUALIB_API void tolua_pushvec4(lua_State *L, float x, float y, float z, float w)
{
	lua_getref(L, LUA_RIDX_PACKVEC4);
	lua_pushnumber(L, x);
	lua_pushnumber(L, y);
	lua_pushnumber(L, z);
	lua_pushnumber(L, w);
	lua_call(L, 4, 1);
}

LUALIB_API void tolua_pushquat(lua_State *L, float x, float y, float z, float w)
{
	lua_getref(L, LUA_RIDX_PACKQUAT);
	lua_pushnumber(L, x);
	lua_pushnumber(L, y);
	lua_pushnumber(L, z);
	lua_pushnumber(L, w);
	lua_call(L, 4, 1);
}

LUALIB_API void tolua_pushclr(lua_State *L, float r, float g, float b, float a)
{
	lua_getref(L, LUA_RIDX_PACKCLR);
	lua_pushnumber(L, r);
	lua_pushnumber(L, g);
	lua_pushnumber(L, b);
	lua_pushnumber(L, a);
	lua_call(L, 4, 1);
}

LUALIB_API void tolua_pushlayermask(lua_State *L, int mask)
{
    lua_getref(L, LUA_RIDX_PACKLAYERMASK);
    lua_pushnumber(L, mask);
    lua_call(L, 1, 1);
}


LUA_API const char* tolua_tolstring(lua_State *L, int index, int* len) 
{
    size_t sz;
    const char *ret = lua_tolstring(L, index, &sz);
    *len = (int)sz;
    return ret;
}

LUA_API void tolua_pushlstring(lua_State *L, const char *s, int l)
{
    lua_pushlstring(L, s, (size_t)l);
}

LUA_API void* tolua_newuserdata(lua_State *L, int sz)
{
    return lua_newuserdata(L, (size_t)sz);    
}

LUA_API int tolua_objlen(lua_State *L, int idx)
{
    size_t len = lua_objlen(L, idx);
    return (int)len;
}

LUA_API bool tolua_toboolean(lua_State *L, int idx) 
{
    int value = lua_toboolean(L, idx);
    return value == 0 ? false : true;
}

LUA_API int32_t tolua_tointeger(lua_State *L, int idx) 
{
    return (int32_t)lua_tointeger(L, idx);
}

LUALIB_API int tolua_loadbuffer(lua_State *L, const char *buff, int sz, const char *name)
{
    return luaL_loadbuffer(L, buff, (size_t)sz, name);
}

static int _lua_getfield(lua_State *L)
{
    const char *name = lua_tostring(L, 2);    
    lua_getfield(L, 1, name);    
    return 1;
}
        
LUA_API int tolua_getfield(lua_State *L, int idx, const char *field)
{
    idx = abs_index(L, idx);    
    lua_pushcfunction(L, _lua_getfield);
    lua_pushvalue(L, idx);
    lua_pushstring(L, field);
    return lua_pcall(L, 2, 1, 0);
}

static int _lua_setfield(lua_State *L)
{
    const char *name = lua_tostring(L, 2);
    lua_setfield(L, 1, name);
    return 0;
}

LUA_API int tolua_setfield(lua_State *L, int idx, const char *key)        
{
    int top = lua_gettop(L);
    idx = abs_index(L, idx);
    lua_pushcfunction(L, _lua_setfield);
    lua_pushvalue(L, idx);
    lua_pushstring(L, key);
    lua_pushvalue(L, top);
    lua_remove(L, top);
    return lua_pcall(L, 3, -1, 0);
}

static int _lua_gettable(lua_State *L)
{    
    lua_gettable(L, 1);    
    return 1;
}

LUA_API int tolua_gettable(lua_State *L, int idx)
{
    int top = lua_gettop(L);
    idx = abs_index(L, idx);
    lua_pushcfunction(L, _lua_gettable);
    lua_pushvalue(L, idx);
    lua_pushvalue(L, top);
    lua_remove(L, top);
    return lua_pcall(L, 2, -1, 0);
}

static int _lua_settable(lua_State *L)
{
    lua_settable(L, 1);
    return 0;
}

LUA_API int tolua_settable(lua_State *L, int idx)
{
    int top = lua_gettop(L);
    idx = abs_index(L, idx);
    lua_pushcfunction(L, _lua_settable);
    lua_pushvalue(L, idx);
    lua_pushvalue(L, top - 1);
    lua_pushvalue(L, top);
    lua_remove(L, top);
    lua_remove(L, top - 1);
    return lua_pcall(L, 3, -1, 0);
}

static int tolua_closure(lua_State *L)
{
    lua_CFunction fn = (lua_CFunction)lua_tocfunction(L, lua_upvalueindex(2));
    int r = fn(L);    
    
    if (lua_toboolean(L, lua_upvalueindex(1)))
    {
        lua_pushboolean(L, 0);
        lua_replace(L, lua_upvalueindex(1));
        return lua_error(L);
    }
    
    return r;
}

//hack for luac, 避免luac error破坏包裹c#函数的异常块(luajit采用的是类似c++异常)
LUA_API int tolua_pushcfunction(lua_State *L, lua_CFunction fn)
{        
    // .. -> name -> new_table1 -> rereference_table -> name
    lua_pushboolean(L, 0);      // .. -> 0
    lua_pushcfunction(L, fn);   // .. -> 0 -> fn
    lua_pushcclosure(L, tolua_closure, 2); // .. -> tolua_closure
    return 0;
}

static int tolua_pusherror(lua_State *L, const char *fmt, ...)
{
    va_list argp;
    va_start(argp, fmt);
    luaL_where(L, 1);
    lua_pushvfstring(L, fmt, argp);
    va_end(argp);
    lua_concat(L, 2);
    return 1;
}

LUALIB_API int tolua_argerror(lua_State *L, int narg, const char *extramsg)
{
    lua_Debug ar;
    
    if (!lua_getstack(L, 0, &ar))  /* no stack frame? */
    {
        return tolua_pusherror(L, "bad argument #%d (%s)", narg, extramsg);        
    }

    lua_getinfo(L, "n", &ar);

    if (strcmp(ar.namewhat, "method") == 0) 
    {
        narg--;  /* do not count `self' */

        if (narg == 0)  /* error is in the self argument itself? */
        {
            return tolua_pusherror(L, "calling " LUA_QS " on bad self (%s)", ar.name, extramsg);
        }
    }

    if (ar.name == NULL)
    {
        ar.name = "?";
    }    

    return tolua_pusherror(L, "bad argument #%d to " LUA_QS " (%s)", narg, ar.name, extramsg);
}

LUALIB_API int tolua_error(lua_State *L, const char *msg)
{
    lua_pushboolean(L, 1);
    lua_replace(L, lua_upvalueindex(1));
    lua_pushstring(L, msg);
    return 1;
}

LUALIB_API int tolua_getn(lua_State *L, int i)
{
    return tolua_objlen(L, i);
}

LUALIB_API int tolua_strlen(const char *str)
{
    if (str == NULL)
    {
        return 0;
    }

    int len = (int)strlen(str);
    return len;
}

// 是否加载过一个模块了，没加载过，但是有，加载一下。返回true
// 没加载过，并且没有，直接返回false
static bool _preload(lua_State *L)
{    
    // table -> key -> nil 或者 多个metatable
    lua_settop(L, 2);                                                                       // table -> key 
    lua_getmetatable(L, 1);                                                                 // table -> key -> mt_table
    lua_pushstring(L, ".name");             //stack: t key mt ".name"                       // table -> key -> mt_table -> ".name"
    lua_rawget(L, -2);                      //stack: t key mt space                         // table -> key -> mt_table -> mt_table[".name"]

    // 获取到的name不是空
    if (!lua_isnil(L, -1))                  //stack: t key mt space
    {                      
        lua_getref(L, LUA_RIDX_PRELOAD);    //stack: t key mt space preload                 // table -> key -> mt_table -> mt_table[".name"] -> preload_table
        lua_pushvalue(L, -2);               //stack: t key mt space preload space           // table -> key -> mt_table -> mt_table[".name"] -> preload_table -> mt_table[".name"]
        lua_pushstring(L, ".");             //stack: t key mt space preload space.          // table -> key -> mt_table -> mt_table[".name"] -> preload_table -> mt_table[".name"] -> "."
        lua_pushvalue(L, 2);                //stack: t key mt space preload space.key       // table -> key -> mt_table -> mt_table[".name"] -> preload_table -> mt_table[".name"] -> "." -> key
                                                                                            // local key1 = mt_table[".name"]+"."+key
        lua_concat(L, 3);                   //stack: t key mt space preload key1            // table -> key -> mt_table -> mt_table[".name"] -> preload_table -> key1
        lua_pushvalue(L, -1);               //stack: t key mt space preload key1 key1       // table -> key -> mt_table -> mt_table[".name"] -> preload_table -> key1 -> key1
        lua_rawget(L, -3);                  //stack: t key mt space preload key1 value      // table -> key -> mt_table -> mt_table[".name"] -> preload_table -> key1 -> preload_table[key1]

        // 如果不为空
        if (!lua_isnil(L, -1)) 
        {      
            lua_pop(L, 1);                      //stack: t key mt space preload key1             // table -> key -> mt_table -> mt_table[".name"] -> preload_table -> key1
            lua_getref(L, LUA_RIDX_REQUIRE);    //stack: t key mt space preload key1 require     // table -> key -> mt_table -> mt_table[".name"] -> preload_table -> key1 -> require_func
            lua_pushvalue(L, -2);                                                                // table -> key -> mt_table -> mt_table[".name"] -> preload_table -> key1 -> require_func -> key1
            // 加载一下对应模块
            lua_call(L, 1, 1);                                                                   
            return true;
        }        
    }

    // 获取到name是空，直接返回 false. 表示没加载过
    lua_settop(L, 2); 
    return false;
}


// 优先获取peer表中东西
static int class_index_event(lua_State *L)
{
    // table -> key
    // table 调用的userdata对象
    // key 要获取的字段
    // gameObject.name : table = gameObject, key = "name"
	int t = lua_type(L, 1);

    // 一般userdata的话，是C#那边的对象，存到C#这边的时候，设置环境为全局环境了
    // tolua_newudata 中设置的
    // #define TOLUA_NOPEER    	LUA_REGISTRYINDEX 

    // &gettag 和 &settag, 在tolua_variable设置的，数据方法

    // 如果 obj 是一个userdata
    if (t == LUA_TUSERDATA)
    {    	
        lua_getfenv(L,1);                                                           // table -> key -> table env

        // 如果不等于全局环境, 一般都等于全局环境的，
        // 如果不是全局环境，就是设置了peer表。调用tolua.setpeer。
        // 获取的就是peer表
        // table env = peer 表
        if (!lua_rawequal(L, -1, TOLUA_NOPEER))     // stack: t k env               // 
        {
            // 如果是一个table, 如果不是table, 直接走下面逻辑。不走这里了
            while (lua_istable(L, -1))                       // stack: t k v mt     // table -> key -> peer 表
            {      
                lua_pushvalue(L, 2);                                                // table -> key -> peer 表 -> key
                lua_rawget(L, -2);                                                  // table -> key -> peer 表 -> peer[key]
            
                // 如果peer中获取到了，直接返回
                if (!lua_isnil(L, -1))
                {                    
                    return 1;
                }

                lua_pop(L, 1);                                                      // table -> key -> peer 表
                lua_pushlightuserdata(L, &gettag);                                  // table -> key -> peer 表 -> &gettag
                lua_rawget(L, -2);                      //stack: obj key env tget   // table -> key -> peer 表 -> peer[&gettag]
            
                // 如果获取到&gettag, 是一个table，表示是一个C#对象。走C#那一套
                // &gettag 和 &settag, 在tolua_variable设置的，数据方法
                // 如果是peer表，需要自己调用  tolua.initget(t) or tolua.initset(t) 获取 get 个 set表。然后增加东西。参考examples 17
                if (lua_istable(L, -1))                                         
                {                    
                    lua_pushvalue(L, 2);                //stack: obj key env tget key   // table -> key -> peer 表 -> peer[&gettag] -> key
                    lua_rawget(L, -2);                  //stack: obj key env tget func  // table -> key -> peer 表 -> peer[&gettag] -> peer[&gettag][key]

                    // 如果是一个函数，直接调用，返回
                    if (lua_isfunction(L, -1))
                    {                        
                        lua_pushvalue(L, 1);                                            // table -> key -> peer 表 -> peer[&gettag] -> peer[&gettag][key] -> table
                        lua_call(L, 1, 1);                                              // table -> key -> peer 表 -> peer[&gettag] -> result
                        return 1;
                    }    

                    // 不是一个函数
                    lua_pop(L, 1);                                                      // table -> key -> peer 表 -> peer[&gettag]
                }

                lua_pop(L, 1);                                                          // table -> key -> peer 表

                // 没有获取到meta表
                if (lua_getmetatable(L, -1) == 0)               // stack: t k v mt mt   
                {
                    // table -> key -> peer 表
                    lua_pushnil(L);                                                      // table -> key -> peer 表 -> nil
                }

                // table -> key -> peer 表 -> mt_peer
                lua_remove(L, -2);                              // stack: t k v mt      // table -> key -> (mt_peer or nil)
                // 继续循环获取
            }        
        };

        lua_settop(L,2);                                        				    // table -> key
    	lua_pushvalue(L, 1);						// stack: obj key obj	        // table -> key -> table

        // 压入原表，没有就返回0
    	while (lua_getmetatable(L, -1) != 0)                                        // table -> key -> table -> metaTable
    	{        	
        	lua_remove(L, -2);						// stack: obj key mt            // table -> key -> metaTable

            // 如果是数字，调用.geti方法
			if (lua_isnumber(L,2))                 	// check if key is a numeric value
			{		    
		    	lua_pushstring(L,".geti");                                          // table -> key -> metaTable -> ".geti"
		    	lua_rawget(L,-2);                   // stack: obj key mt func       // table -> key -> metaTable -> mt[".geti"]

                // 如果是一个函数
		    	if (lua_isfunction(L,-1))
		    	{
		        	lua_pushvalue(L,1);                                             // table -> key -> metaTable -> mt[".geti"] -> table
		        	lua_pushvalue(L,2);                                             // table -> key -> metaTable -> mt[".geti"] -> table -> key
		        	lua_call(L,2,1);                                                // table -> key -> metaTable -> mt[".geti"] -> mt[".geti"](table, key)
		        	return 1;
		    	}
			}
			else
        	{
        		lua_pushvalue(L, 2);			    // stack: obj key mt key        // table -> key -> metaTable -> key
        		lua_rawget(L, -2);					// stack: obj key mt value      // table -> key -> metaTable -> metaTable[key]

                // 获取到的值不为空， 直接返回
        		if (!lua_isnil(L, -1))
        		{
        	    	return 1;
        		}
                
                // 获取到空值了
                lua_pop(L, 1);                                                      // table -> key -> metaTable
				lua_pushlightuserdata(L, &gettag);        	                        // table -> key -> metaTable -> &gettag
        		lua_rawget(L, -2);					//stack: obj key mt tget        // table -> key -> metaTable -> &gettag -> metaTable[&gettag]

                // metaTable[&gettag] 是一个table, 具体看 tolua_variable 函数，存放的是name -> func
                // 如果是一个table
        		if (lua_istable(L, -1))
        		{
        	    	lua_pushvalue(L, 2);			//stack: obj key mt tget key    // table -> key -> metaTable -> &gettag -> metaTable[&gettag] -> key
        	    	lua_rawget(L, -2);           	//stack: obj key mt tget value  // table -> key -> metaTable -> &gettag -> metaTable[&gettag] -> metaTable[&gettag][key]

                    // 如果是一个函数
        	    	if (lua_isfunction(L, -1))
        	    	{
        	        	lua_pushvalue(L, 1);                                        // table -> key -> metaTable -> &gettag -> metaTable[&gettag] -> metaTable[&gettag][key] -> table
        	        	lua_call(L, 1, 1);                                          // table -> key -> metaTable -> &gettag -> metaTable[&gettag] -> metaTable[&gettag][key] -> metaTable[&gettag][key](table)
        	        	return 1;
        	    	}                    
        		}
    		}

            lua_settop(L, 3);                                                       // table -> key -> metaTable
            // 执行到这里表示没有获取到，继续循环，获取元表的元表，也就是C#的父类
        }

        lua_settop(L, 2);                                                                   // table -> key
        int *udata = (int*)lua_touserdata(L, 1);

        // table 为空， 报错尝试索引一个nil
        if (*udata == LUA_NULL_USERDATA)
        {
            return luaL_error(L, "attemp to index %s on a nil value", lua_tostring(L, 2));   
        }
        
        // 应该一直为true,
        // 报错key不存在
        if (toluaflags & FLAG_INDEX_ERROR)
        {
            return luaL_error(L, "field or property %s does not exist", lua_tostring(L, 2));
        }        
    }

    // 如果 obj 是一个table
    else if(t == LUA_TTABLE)
    {
    	lua_pushvalue(L,1);                          //stack: obj key obj                   // table -> key -> table

		while (lua_getmetatable(L, -1) != 0)         //stack: obj key obj mt                // table -> key -> table -> mt_table
    	{
        	lua_remove(L, -2);						// stack: obj key mt                    // table -> key -> mt_table

        	lua_pushvalue(L, 2);			    	// stack: obj key mt key                // table -> key -> mt_table -> key
        	lua_rawget(L, -2);						// stack: obj key mt value              // table -> key -> mt_table -> mt_table[key]

            // mt_table[key] 不为nil
        	if (!lua_isnil(L, -1))                                                          
        	{
                // 如果是一个函数, 缓存到table中，不是的话，直接返回
        		if (lua_isfunction(L, -1))			//cache static function
        		{
        			lua_pushvalue(L, 2);           // stack: obj key mt value key           // table -> key -> mt_table -> mt_table[key] -> key
        			lua_pushvalue(L, -2);          // stack: obj key mt value key value     // table -> key -> mt_table -> mt_table[key] -> key -> mt_table[key]
                    // table[key] = mt_table[key]
        			lua_rawset(L, 1);                                                       // table -> key -> mt_table -> mt_table[key]
        		}

        	    return 1;
        	}        	
        	
            // table -> key -> mt_table -> mt_table[key] or nil
            lua_pop(L, 1);                                                                  // table -> key -> mt_table
			lua_pushlightuserdata(L, &gettag);        	                                    // table -> key -> mt_table -> &gettag
        	lua_rawget(L, -2);						//stack: obj key mt tget                // table -> key -> mt_table -> mt_table[&gettag]

            // 如果是一个table
        	if (lua_istable(L, -1))
        	{
        	    lua_pushvalue(L, 2);				//stack: obj key mt tget key            // table -> key -> mt_table -> mt_table[&gettag] -> key
        	    lua_rawget(L, -2);           		//stack: obj key mt tget value          // table -> key -> mt_table -> mt_table[&gettag] -> mt_table[&gettag][key]

                // 如果是一个函数
        	    if (lua_isfunction(L, -1))
        	    {        	        	    
                    lua_pushvalue(L, 1);                                                    // table -> key -> mt_table -> mt_table[&gettag] -> mt_table[&gettag][key] -> table
        	        lua_call(L, 1, 1);                                                      // table -> key -> mt_table -> mt_table[&gettag] -> result
        	        return 1;
        	    }
        	}

        	lua_settop(L, 3);                                                               // table -> key -> mt_table, 继续重复获取metatable
    	}        
        
        // table -> key -> nil 或者 多个metatable
        if (_preload(L))
        {
            return 1;
        }          
        
        // 报错下，没找到
        if (toluaflags & FLAG_INDEX_ERROR)
        {
            return luaL_error(L, "field or property %s does not exist", lua_tostring(L, 2));               
        }      
    }

    // 增加一个nil 
    lua_pushnil(L);
    return 1;
}

static void storeatubox (lua_State *L, int lo)
{
    lua_getfenv(L, lo);                         // stack: t, k, v, _env

    if (lua_rawequal(L, -1, TOLUA_NOPEER)) 
    {
        lua_pop(L, 1);
        return;
        //lua_newtable(L);                        // stack: t, k, v, t
        //lua_pushvalue(L, -1);                   // stack: t, k, v, t, t        
        //lua_setfenv(L, lo);                     // stack: t, k, v, t
    };

    lua_insert(L, -3);
    lua_settable(L, -3);
    lua_pop(L, 1);
}

static int class_newindex_event(lua_State *L)
{
    // table -> key -> value

	int t = lua_type(L, 1);

    // 如果是userdata
	if (t == LUA_TUSERDATA)
    {
        bool useEnv = false;
        // 获取peer表
        lua_getfenv(L, 1);                                                              // table -> key -> value -> table.peer

        // 获取到peer表了, 尝试通过peer表设置
        if (!lua_rawequal(L, -1, TOLUA_NOPEER)) 
        {          
            // 标记使用env了，也就是peer了   
            useEnv = true;

            // 如果是一个table
            while (lua_istable(L, -1))                       // stack: t k v mt         // table -> key -> value -> table.peer
            {       
                lua_pushvalue(L, 2);                        // stack: t k v mt k         // table -> key -> value -> table.peer -> key
                lua_rawget(L, -2);                          // stack: t k v mt value     // table -> key -> value -> table.peer -> table.peer[key]

                // 如果不为nil
                if (!lua_isnil(L, -1))
                {                    
                    lua_pop(L, 1);                                                        // table -> key -> value -> table.peer
                    lua_insert(L, -3);                                                    // table -> table.peer -> key -> value
                    // table.peer[key] = value
                    lua_rawset(L, -3);                                                    // table -> table.peer
                    return 0;
                }

                // table -> key -> value -> table.peer -> nil
                lua_pop(L, 1);                                                            // table -> key -> value -> table.peer
                lua_pushlightuserdata(L, &settag);              // stack: t k v mt tset   // table -> key -> value -> table.peer -> &settag
                lua_rawget(L, -2);                                                        // table -> key -> value -> table.peer -> table.peer[&settag]

                // 如果获取到是一个table， 表示是一个userdata把
                if (lua_istable(L, -1)) 
                {
                    // table -> key -> value -> table.peer -> table.peer[&settag]
                    lua_pushvalue(L, 2);                         // stack: t k v mt tset k  // table -> key -> value -> table.peer -> table.peer[&settag] -> key
                    lua_rawget(L, -2);                                                      // table -> key -> value -> table.peer -> table.peer[&settag] -> key -> table.peer[&settag][key]

                    // 是一个函数，直接调用设置函数
                    if (lua_isfunction(L, -1)) 
                    {          
                        // table -> key -> value -> table.peer -> table.peer[&settag] -> key -> table.peer[&settag][key]                
                        lua_pushvalue(L, 1);                                                // table -> key -> value -> table.peer -> table.peer[&settag] -> key -> table.peer[&settag][key] -> table
                        lua_pushvalue(L, 3);                                                // table -> key -> value -> table.peer -> table.peer[&settag] -> key -> table.peer[&settag][key] -> table -> value
                        lua_call(L, 2, 0);
                        return 0;
                    }

                    // table -> key -> value -> table.peer -> table.peer[&settag] -> key -> nil
                    lua_pop(L, 1);                              // stack: t k v mt tset  // table -> key -> value -> table.peer -> table.peer[&settag] -> key
                }  

                // table -> key -> value -> table.peer -> table.peer[&settag] or nil
                lua_pop(L, 1);                                  // stack: t k v mt      // table -> key -> value -> table.peer

                // 没有metatable
                if (lua_getmetatable(L, -1) == 0)               // stack: t k v mt mt
                {
                    // table -> key -> value -> table.peer
                    lua_pushnil(L);                                                     // table -> key -> value -> table.peer -> nil
                }

                // // table -> key -> value -> table.peer -> mt_table.peer or nil
                lua_remove(L, -2);                              // stack: t k v mt      // table -> key -> value -> mt_table.peer or nil
            }              
        }


        // useEnv 标记是否用了peer访问
        // peer 表设置失败， 
        lua_settop(L, 3);                                                               // table -> key -> value
    	lua_getmetatable(L,1);                                                          // table -> key -> value -> mt_table

        // 如果是table
    	while (lua_istable(L, -1))                			// stack: t k v mt
    	{        	
    		if (lua_isnumber(L, 2))                         // 如果key是number, 调用.seti方法，beginClass的时候，在C#端注册的
    		{
				lua_pushstring(L,".seti");                                              // table -> key -> value -> mt_table -> ".seti"
                lua_rawget(L,-2);                      		// stack: obj key mt func   // table -> key -> value -> mt_table -> ".seti" -> mt_table[".seti"]

                // 如果是函数，调用且返回
                if (lua_isfunction(L,-1))
                {
                    lua_pushvalue(L,1);                                                 // table -> key -> value -> mt_table -> ".seti" -> mt_table[".seti"] -> table
                    lua_pushvalue(L,2);                                                 // table -> key -> value -> mt_table -> ".seti" -> mt_table[".seti"] -> table -> key
                    lua_pushvalue(L,3);                                                 // table -> key -> value -> mt_table -> ".seti" -> mt_table[".seti"] -> table -> key -> value
                    lua_call(L,3,0);                                                    // table -> key -> value -> mt_table -> ".seti"
                }

                // 不管是否设置成功，都直接返回了
                return 0;        
    		}
            // 不是number
    		else
        	{
                // &settag 具体看 tolua_variable 函数
        		lua_pushlightuserdata(L, &settag);                                      // table -> key -> value -> mt_table -> &settag
        		lua_rawget(L, -2);                      	// stack: t k v mt tset     // table -> key -> value -> mt_table -> mt_table[&settag]

                // 如果是一个table
        		if (lua_istable(L, -1))                                             
        		{
            		lua_pushvalue(L, 2);                                                // table -> key -> value -> mt_table -> mt_table[&settag] -> key
            		lua_rawget(L, -2);                     	// stack: t k v mt tset func// table -> key -> value -> mt_table -> mt_table[&settag] -> key -> mt_table[&settag][key]

                    // 如果是函数
            		if (lua_isfunction(L, -1))
            		{
                		lua_pushvalue(L, 1);                                            // table -> key -> value -> mt_table -> mt_table[&settag] -> key -> mt_table[&settag][key] -> table
                		lua_pushvalue(L, 3);                                            // table -> key -> value -> mt_table -> mt_table[&settag] -> key -> mt_table[&settag][key] -> table -> value
                        // 调用设置函数
                		lua_call(L, 2, 0);
                		return 0;
            		}

                    // table -> key -> value -> mt_table -> mt_table[&settag] -> key -> nil
            		lua_pop(L, 1);                          // stack: t k v mt tset     // table -> key -> value -> mt_table -> mt_table[&settag] -> key
        		}

                // table -> key -> value -> mt_table -> mt_table[&settag] or nil
        		lua_pop(L, 1);                              // stack: t k v mt          // table -> key -> value -> mt_table

        		if (lua_getmetatable(L, -1) == 0)           // stack: t k v mt mt       
        		{
                    // table -> key -> value -> mt_table -> nil
            		lua_pushnil(L);
        		}

                // table -> key -> value -> mt_table -> nil or mt_mt_table
        		lua_remove(L, -2);                          // stack: t k v mt          // table -> key -> value -> nil or mt_mt_table
        	}
    	}


        // metatable中没找到设置方法
        lua_settop(L, 3);                                       // stack: t k v         // table -> key -> value
        int* udata = (int*)lua_touserdata(L, 1);
        
        // 报错
        if (*udata == LUA_NULL_USERDATA)
        {
            return luaL_error(L, "attemp to index %s on a nil value", lua_tostring(L, 2));   
        }        

        // 如果用了env, peer表，并且自己不为空
        // 设置到peer表中
        if (useEnv) 
        {
            lua_getfenv(L, 1);                         // stack: t, k, v, _env          // table -> key -> value -> table.peer
            lua_insert(L, -3);                                                          // table -> table.peer -> key -> value
            // table.peer[key] = value
            lua_settable(L, -3);                                                        // table -> table.peer
            lua_pop(L, 1);                                                              // table
            return 0;
        }                       
	}
    // 不是userdata, 是普通table
    // table -> key -> value
	else if (t == LUA_TTABLE)
	{
		lua_getmetatable(L, 1);								// stack: t k v mt          // table -> key -> value -> mt_table

		while (lua_istable(L, -1))                			// stack: t k v mt 
    	{  		
			lua_pushlightuserdata(L, &settag);				// stack: t k v mt tset
        	lua_rawget(L, -2);       

        	if (lua_istable(L,-1)) 
        	{
            	lua_pushvalue(L,2);  						// stack: t k v mt tset k
            	lua_rawget(L,-2);

            	if (lua_isfunction(L,-1)) 
            	{  
                	lua_pushvalue(L,1); 
                	lua_pushvalue(L,3); 
                	lua_call(L,2,0);
                	return 0;
            	}

            	lua_pop(L, 1);                          	// stack: t k v mt tset 
        	}  

			lua_pop(L, 1);                              	// stack: t k v mt

        	if (lua_getmetatable(L, -1) == 0)           	// stack: t k v mt mt
        	{
            	lua_pushnil(L);
        	}

        	lua_remove(L, -2);                          	// stack: t k v mt
        }      
	}

    lua_settop(L, 3); 
    return luaL_error(L, "field or property %s does not exist", lua_tostring(L, 2));      
}

static int enum_index_event (lua_State *L)
{
	lua_getmetatable(L, 1);									//stack: t, k, mt

	if (lua_istable(L, -1))
	{
		lua_pushvalue(L, 2);								//stack: t, k, mt, k
		lua_rawget(L, -2);									//stack: t, k, mt, v

		if (!lua_isnil(L, -1))
		{		
			return 1;
		}

		lua_pop(L, 1);										//stack: t, k, mt
		lua_pushlightuserdata(L, &gettag);		
		lua_rawget(L, -2); 									//stack: t, k, mt, tget

		if (lua_istable(L,-1)) 
		{
            lua_pushvalue(L,2);  							//stack: t k mt tget k
            lua_rawget(L,-2);								//stack: t k mt tget v

           	if (lua_isfunction(L,-1)) 
			{  
				lua_call(L, 0, 1);					
				lua_pushvalue(L,2); 				
				lua_pushvalue(L,-2); 				
				lua_rawset(L, 3);		
				return 1;			
			}

			lua_pop(L, 1);
        }			
	}

	lua_settop(L, 2);
	lua_pushnil(L);
	return 1;	
}

static int enum_newindex_event(lua_State *L)
{	
	luaL_error(L, "the left-hand side of an assignment must be a variable, a property or an indexer");
    return 1;
}

static int static_index_event(lua_State *L)
{    
    lua_pushvalue(L, 2);                    //stack: t key key
    lua_rawget(L, 1);                       //stack: t key value        

    if (!lua_isnil(L, -1))
    {
        return 1;
    }

    lua_pop(L, 1);            
    lua_pushlightuserdata(L, &gettag);      //stack: t key tag    
    lua_rawget(L, 1);                       //stack: t key tget

    if (lua_istable(L, -1))
    {
        lua_pushvalue(L, 2);                //stack: obj key tget key
        lua_rawget(L, -2);                  //stack: obj key tget func 

        if (lua_isfunction(L, -1))
        {                       
            lua_call(L, 0, 1);
            return 1;
        }        
    }
    
    lua_settop(L, 2);

    if (_preload(L))
    {
        return 1;
    }
    
    if (toluaflags & FLAG_INDEX_ERROR)
    {
        luaL_error(L, "field or property %s does not exist", lua_tostring(L, 2));    
    }

    return 1;
}

static int static_newindex_event(lua_State *L)
{
    lua_pushlightuserdata(L, &settag);              //stack: t k v tag
    lua_rawget(L, 1);                               //stack: t k v tset

    if (lua_istable(L,-1)) 
    {
        lua_pushvalue(L, 2);                         //stack: t k v tset k
        lua_rawget(L, -2);                           //stack: t k v tset func

        if (lua_isfunction(L, -1))
        {    
            lua_pushvalue(L,1); 
            lua_pushvalue(L,3); 
            lua_call(L,2,0);
            return 0;
        }
    }

    lua_settop(L, 3); 
    luaL_error(L, "field or property %s does not exist", lua_tostring(L, 2));
    return 1;
}

// peer表的元表的__index方法
// local peer = {}
// local peer = tolua.setpeer(u, peer)
// 当使用 peer.base or u.base 时。 会获取到一个 "空表" baseT
// baseT[&vptr] = u ,表里面只有这样一个数据。
// baseT的元表是_G[LUA_RIDX_VPTR]。 具体参考 tolua_bnd_setpeer 方法
// 访问不存在的属性，就会调用到这个方法，
static int vptr_index_event(lua_State *L)
{    
    // local t = getmetatable(peertable)
    // t -> key
    lua_pushlightuserdata(L, &vptr);                                             // t -> key -> &vptr
    lua_rawget(L, 1);                                   // stack: t key u        // t -> key -> t[&vptr]
    // local userdata = t[&vptr]
    lua_replace(L, 1);                                  // stack: u key          // userdata -> key
    lua_pushvalue(L, 1);                                // stack: u key u        // userdata -> key -> userdata

    // 获取 metadata
    while (lua_getmetatable(L, -1) != 0)
    {
        // userdata -> key -> userdata -> mt_userdata
        lua_remove(L, -2);                              // stack: u key mt          // userdata -> key -> mt_userdata
        lua_pushvalue(L, 2);                            // stack: u key mt key      // userdata -> key -> mt_userdata -> key
                                                                                    // local value = mt_userdata[key]
        lua_rawget(L, -2);                              // stack: u key mt value    // userdata -> key -> mt_userdata -> value

        // 不是 nil ,直接返回
        if (!lua_isnil(L, -1))
        {
            return 1;
        }
        
        lua_pop(L, 1);                                                              // userdata -> key -> mt_userdata
        lua_pushlightuserdata(L, &gettag);                                          // userdata -> key -> mt_userdata -> &gettag
                                                                                    // local gettable = mt_userdata[&gettag]
        lua_rawget(L, -2);                              //stack: u key mt tget      // userdata -> key -> mt_userdata -> gettable

        // 如果是一个table
        if (lua_istable(L, -1))
        {
            lua_pushvalue(L, 2);                        //stack: obj key mt tget key        // userdata -> key -> mt_userdata -> gettable -> key
            lua_rawget(L, -2);                          //stack: obj key mt tget value      // userdata -> key -> mt_userdata -> gettable -> gettable[key]

            // 一般是一个函数，直接调用就行了， 不是函数，就不管，
            if (lua_isfunction(L, -1))
            {
                lua_pushvalue(L, 1);
                lua_call(L, 1, 1);
                return 1;
            }
        }

        lua_settop(L, 3);                                                                   // userdata -> key -> mt_userdata
        // 继续获取mt_table的mt_table
    }

    // 没找到，返回一个nil
    lua_settop(L, 2);
    lua_pushnil(L);
    return 1;
}

// peer表的元表的__newindex方法
// vptr table的__newIndex方法
// local peer = {}
// local peer = tolua.setpeer(u, peer)
// 当使用 peer.base or u.base 时。 会获取到一个 "空表" baseT
// baseT[&vptr] = u ,表里面只有这样一个数据。
// baseT的元表是_G[LUA_RIDX_VPTR]。 具体参考 tolua_bnd_setpeer 方法
// 设置不存在的属性，就会调用到这个方法，
static int vptr_newindex_event(lua_State *L)
{    
    // local vptr_t = getmetatable(peertable)
    // vptr_t -> key -> value
    lua_pushlightuserdata(L, &vptr);                                                // vptr_t -> key -> value -> &vptr
                                                                                    // local userdata = vptr_t[&vptr]
    lua_rawget(L, 1);                                   // stack: t key v u         // vptr_t -> key -> value -> userdata
    lua_getmetatable(L, -1);                                                        // vptr_t -> key -> value -> userdata -> mt_userdata

    while (lua_istable(L, -1))                          // stack: u k v mt
    {           
        lua_pushlightuserdata(L, &settag);                                          // vptr_t -> key -> value -> userdata -> mt_userdata -> &settag
                                                                                    // local set_table = mt_userdata[&settag]
        lua_rawget(L, -2);                              // stack: u k v mt tset     // vptr_t -> key -> value -> userdata -> mt_userdata -> set_table

        if (lua_istable(L, -1))
        {
            lua_pushvalue(L, 2);                                                    // vptr_t -> key -> value -> userdata -> mt_userdata -> set_table -> key
                                                                                    // local setfunc = set_table[key]
            lua_rawget(L, -2);                          // stack: u k v mt tset func// vptr_t -> key -> value -> userdata -> mt_userdata -> set_table -> setfunc

            // 一般都是一个func， 不是就不管
            if (lua_isfunction(L, -1))
            {
                lua_pushvalue(L, 4);                                                // vptr_t -> key -> value -> userdata -> mt_userdata -> set_table -> setfunc -> userdata
                lua_pushvalue(L, 3);                                                // vptr_t -> key -> value -> userdata -> mt_userdata -> set_table -> setfunc -> userdata -> value
                lua_call(L, 2, 0);
                return 0;
            }

            lua_pop(L, 1);                              // stack: t k v mt tset     // vptr_t -> key -> value -> userdata -> mt_userdata -> set_table
        }

        lua_pop(L, 1);                                  // stack: t k v mt          // vptr_t -> key -> value -> userdata -> mt_userdata

        if (lua_getmetatable(L, -1) == 0)               // stack: t k v mt mt       // vptr_t -> key -> value -> userdata -> mt_userdata -> mt_mt_userdata
        {
            lua_pushnil(L);
        }

        // vptr_t -> key -> value -> userdata -> mt_userdata -> mt_mt_userdata or nil
        lua_remove(L, -2);                              // stack: t k v mt             // vptr_t -> key -> value -> userdata -> mt_mt_userdata or nil。 继续循环查找父类
    }

    lua_settop(L, 3);    // vptr_t -> key -> value 
    return 1;
}

// 判断一个表 是不是 vptr table
// 也就是 peer表的 metatable
LUALIB_API bool tolua_isvptrtable(lua_State *L, int index)
{    
    lua_pushlightuserdata(L, &vptr);
    lua_rawget(L, index);
    bool flag = lua_isnil(L, -1) ? false : true;
    lua_pop(L, 1);
    return flag;
}

// 增加__index方法
LUALIB_API void tolua_setindex(lua_State *L)
{
	lua_pushstring(L, "__index");
	lua_pushcfunction(L, class_index_event);
	lua_rawset(L, -3);
}

// 增加__newindex方法
LUALIB_API void tolua_setnewindex(lua_State *L)
{
	lua_pushstring(L, "__newindex");
	lua_pushcfunction(L, class_newindex_event);
	lua_rawset(L, -3);
}


// index : C#端  存储 ObjectTranslator.objects 的中下标
// LUA_RIDX_UBOX 和 C# 端 ObjectTranslator.objects 对应
LUALIB_API bool tolua_pushudata(lua_State *L, int index)
{
	lua_getref(L, LUA_RIDX_UBOX);			// stack: ubox
	lua_rawgeti(L, -1, index); 				// stack: ubox, obj

	if (!lua_isnil(L, -1))
	{
		lua_remove(L, -2); 					// stack: obj
		return true;
	}

	lua_pop(L, 2);
	return false;
}


// LUA_RIDX_UBOX 和 C# 端 ObjectTranslator.objects 对应
// index : C#端  存储 ObjectTranslator.objects 的中下标
// metaRef : obj 类型在lua端的 reference
LUALIB_API void tolua_pushnewudata(lua_State *L, int metaRef, int index)
{
	lua_getref(L, LUA_RIDX_UBOX);   // 全局变量中获取 LUA_RIDX_UBOX处的table,并且放在栈上 .. -> global[LUA_RIDX_UBOX]
    // *userdata = index
    // *meta_ref = obj.class.reference
	tolua_newudata(L, index);       // .. -> global[LUA_RIDX_UBOX] -> *userdata
	lua_getref(L, metaRef);         // .. -> global[LUA_RIDX_UBOX] -> *userdata -> *meta_ref
    // setmetatable(*userdata, meta_ref)。
	lua_setmetatable(L, -2);        // .. -> global[LUA_RIDX_UBOX] -> *userdata
	lua_pushvalue(L, -1);           // .. -> global[LUA_RIDX_UBOX] -> *userdata -> *userdata
    // global[LUA_RIDX_UBOX][index] = *userdata;
	lua_rawseti(L, -3, index);      // .. -> global[LUA_RIDX_UBOX] -> *userdata
	lua_remove(L, -2);	            // .. -> *userdata
}

// 
static int module_index_event(lua_State *L)
{    

/* lua
metatable.__index = func(table, index) end
*/

    lua_pushvalue(L, 2);                    //stack: t key key
    lua_rawget(L, 1);                       //stack: t key value            

    if (!lua_isnil(L, -1))
    {
        return 1;
    }

    lua_pop(L, 1);                          //stack: t key 
    lua_pushstring(L, ".name");             //stack: t key ".name"
    lua_rawget(L, 1);        

    if (!lua_isnil(L, -1))                  //stack: t key space
    {                      
        lua_getref(L, LUA_RIDX_PRELOAD);    //stack: t key space preload
        lua_pushvalue(L, -2);
        lua_pushstring(L, ".");
        lua_pushvalue(L, 2);
        lua_concat(L, 3);                   //stack: t key space preload key
        lua_pushvalue(L, -1);               //stack: t key space preload key1 key1
        lua_rawget(L, -3);                  //stack: t key space preload key1 value        

        if (!lua_isnil(L, -1)) 
        {      
            lua_pop(L, 1);                      //stack: t key space preload key1
            lua_getref(L, LUA_RIDX_REQUIRE);
            lua_pushvalue(L, -2);
            lua_call(L, 1, 1);                    
        }
        else
        {
            lua_pushnil(L);                            
        }
    }
    
    return 1;
}

typedef struct stringbuffer 
{        
  const char *buffer;
  size_t len;
} stringbuffer;

static stringbuffer sb;

void initmodulebuffer()
{
    sb.len = 0;
    sb.buffer = NULL;
}

void pushmodule(lua_State *L, const char *str)
{    

    // [prestack] -> {newtable} -> name -> ".name"

    luaL_Buffer b;
    luaL_buffinit(L, &b);


    // 把之前的module名字附加进去 到 b 中
    // 例如 system.string.xxx
    if (sb.len > 0)
    {
        luaL_addlstring(&b, sb.buffer, sb.len);
        luaL_addchar(&b, '.');
    }

    // 添加这次的str到 b 中
    luaL_addstring(&b, str);
    luaL_pushresult(&b);    // [prestack] -> {newtable} -> name -> ".name" -> &b(存放模块完整名字的地址) -> &b .. b.lvl
    sb.buffer = lua_tolstring(L, -1, &sb.len);    
}

LUALIB_API bool tolua_beginmodule(lua_State *L, const char *name)
{
    // name == null 将全局table push进入栈
    // name != null, 创建一个table, 放入栈

    // [prestack]

    if (name != NULL)
    {                
        lua_pushstring(L, name);			//stack key         // [prestack] -> name
        // [prestack][-1] 至少都是提个全局那个table, 或者 一个module的table。
        // 从moule 中获取 module[name]
        lua_rawget(L, -2);					//stack value       // [prestack] -> [prestack][-1][name]

        // 当前模块不存在
        if (lua_isnil(L, -1))               // module[name] == nil
        {
            lua_pop(L, 1);                                                      // [prestack]
            lua_newtable(L);				//stack table                       // [prestack] -> {newtable}

            lua_pushstring(L, "__index");                                       // [prestack] -> {newtable} -> "__index"
            lua_pushcfunction(L, module_index_event);                           // [prestack] -> {newtable} -> "__index" -> module_index_event
            // {newtable}[__index] = module_index_event
            lua_rawset(L, -3);                                                  // [prestack] -> {newtable}

            lua_pushstring(L, name);        //stack table name                  // [prestack] -> {newtable} -> name
            lua_pushstring(L, ".name");     //stack table name ".name"          // [prestack] -> {newtable} -> name -> ".name"
            pushmodule(L, name);            //stack table name ".name" module   // [prestack] -> {newtable} -> name -> ".name" -> (*b(存放模块完整名字的地址) .. b.lvl)
            // {newtable}[.name] = *b .. b.lvl
            lua_rawset(L, -4);              //stack table name                  // [prestack] -> {newtable} -> name
            lua_pushvalue(L, -2);			//stack table name table            // [prestack] -> {newtable} -> name -> {newtable}
            // [prestack][-1][name] = {newtable}
            lua_rawset(L, -4);   			//stack table                       // [prestack] -> {newtable}

            lua_pushvalue(L, -1);                                               // [prestack] -> {newtable} -> {newtable}
            // setmetatable({newtable}, {newtable})
            lua_setmetatable(L, -2);                                            // [prestack] -> {newtable}
            return true;
        }

        // [prestack] -> [prestack][-1][name]

        // 当前模块存在
        else if (lua_istable(L, -1))
        {
            // 如果没有元表，新建一个元表，和上面差不多逻辑
            if (lua_getmetatable(L, -1) == 0)
            {
                lua_pushstring(L, "__index");
                lua_pushcfunction(L, module_index_event);
                lua_rawset(L, -3);

                lua_pushstring(L, name);        //stack table name         
                lua_pushstring(L, ".name");     //stack table name ".name"            
                pushmodule(L, name);            //stack table name ".name" module            
                lua_rawset(L, -4);              //stack table name            
                lua_pushvalue(L, -2);           //stack table name table
                lua_rawset(L, -4);              //stack table

                lua_pushvalue(L, -1);
                lua_setmetatable(L, -2);                    
            }
            // 获取到了元表
            else
            {
                // [prestack] -> [prestack][-1][name] -> 当前模块的元表metatable
                lua_pushstring(L, ".name");                 // [prestack] -> 当前模块 -> 当前模块的元表metatable -> ".name"
                lua_gettable(L, -3);                        // [prestack] -> 当前模块 -> 当前模块的元表metatable -> 当前模块[".name"]
                sb.buffer = lua_tolstring(L, -1, &sb.len);  // 填充到sb中                 
                lua_pop(L, 2);                              // [prestack] -> 当前模块               
            }

            return true;
        }

        return false;
    }
    else
    {                
        lua_pushvalue(L, LUA_GLOBALSINDEX);
        return true;
    }                
}

LUALIB_API void tolua_endmodule(lua_State *L)
{
    lua_pop(L, 1);
    int len = (int)sb.len;

    while(len-- >= 0)
    {
        if (sb.buffer[len] == '.')
        {
            sb.len = len;
            return;
        }
    }

    sb.len = 0;
}

static int class_new_event(lua_State *L)
{         
    if (!lua_istable(L, 1))
    {
        return luaL_typerror(L, 1, "table");        
    }

    // obj -> args ... 
    int count = lua_gettop(L); 
    lua_pushvalue(L,1);                 // obj -> obj

    if (lua_getmetatable(L,-1))
    {   
        // obj -> obj -> mt_obj 
        lua_remove(L,-2);              // obj -> obj -> mt_obj   
        // 需要自己注册New函数，来提供创建对象的逻辑，没有New函数，就会报错， UnityEngine.GameObject就提供了       
        lua_pushstring(L, "New");      // obj -> obj -> mt_obj -> New
        lua_rawget(L,-2);              // obj -> obj -> mt_obj -> mt_obj["New"]

        if (lua_isfunction(L,-1))
        {            
            for (int i = 2; i <= count; i++)
            {
                lua_pushvalue(L, i);                    
            }

            lua_call(L, count - 1, 1);
            return 1;
        }

        lua_settop(L,3);
    }    

    return luaL_error(L,"attempt to perform ctor operation failed");    
}

// 将缓存的命名空间 和 指定位置处的值，进行拼接，获取到类完整的名字， 并且压入栈顶
static void _pushfullname(lua_State *L, int pos)
{
    if (sb.len > 0)
    {
        lua_pushlstring(L, sb.buffer, sb.len);
        lua_pushstring(L, ".");
        lua_pushvalue(L,  pos < 0 ? pos - 2 : pos + 2);
        lua_concat(L, 3);
    }
    else
    {
        lua_pushvalue(L, pos);
    }
}

// 标记为已加载，并且放入 LUA_RIDX_LOADED 表中
// 进入函数和结束函数栈的未被改变
static void _addtoloaded(lua_State *L)
{
    lua_getref(L, LUA_RIDX_LOADED);
    _pushfullname(L, -3);
    lua_pushvalue(L, -3);
    lua_rawset(L, -3);
    lua_pop(L, 1);
}

LUALIB_API int tolua_beginclass(lua_State *L, const char *name, int baseType, int ref)
{
    int reference = ref;  // 默认是-1， 也就是LUA_REFNIL， C#那边传的。自己还不存在，没有提前创建过占位table
    lua_pushstring(L, name);       //   .. -> name       
    lua_newtable(L);               //   .. -> name -> table
    _addtoloaded(L); // 标记为已加载，并且放入 LUA_RIDX_LOADED 表中

    if (ref == LUA_REFNIL)        
    {
        lua_newtable(L);
        lua_pushvalue(L, -1);
        reference = luaL_ref(L, LUA_REGISTRYINDEX); 
    }
    else
    {
        lua_getref(L, reference);    
    }

    //   .. -> name -> new_table1 -> rereference_table
    // reference表示自己的的索引


    // 设置父类
    if (baseType != 0)
    {
        lua_getref(L, baseType);        
        lua_setmetatable(L, -2);
    }
           

    //   .. -> name -> new_table1 -> rereference_table

    lua_pushlightuserdata(L, &tag); // .. -> name -> new_table1 -> rereference_table -> &tag
    lua_pushnumber(L, 1);           // .. -> name -> new_table1 -> rereference_table -> &tag -> 1
    // rereference_table[&tag] = 1
    lua_rawset(L, -3);              // .. -> name -> new_table1 -> rereference_table

    lua_pushstring(L, ".name");     // .. -> name -> new_table1 -> rereference_table -> ".name"
    _pushfullname(L, -4);           // .. -> name -> new_table1 -> rereference_table -> ".name" -> （name + ".name"）

    // rereference_table[".name"] = （name + ".name"）
    lua_rawset(L, -3);              // .. -> name -> new_table1 -> rereference_table

    lua_pushstring(L, ".ref");      // .. -> name -> new_table1 -> rereference_table -> ".ref"
    lua_pushinteger(L, reference);  // .. -> name -> new_table1 -> rereference_table -> ".ref" -> reference

    // rereference_table[".ref"] = reference
    lua_rawset(L, -3);              // .. -> name -> new_table1 -> rereference_table

    lua_pushstring(L, "__call");    // .. -> name -> new_table1 -> rereference_table -> "__call"
    lua_pushcfunction(L, class_new_event); // .. -> name -> new_table1 -> rereference_table -> "__call" -> class_new_event

    // rereference_table["__call"] = class_new_event
    lua_rawset(L, -3);              // .. -> name -> new_table1 -> rereference_table

    // 增加__index方法
    tolua_setindex(L);

    // 增加__newIndex 方法
    tolua_setnewindex(L); 

    // .. -> name -> new_table1 -> rereference_table
    return reference;
}


LUALIB_API void tolua_endclass(lua_State *L)
{
    // .. -> name -> new_table1 -> rereference_table
    // setmetatable(new_table1, rereference_table)
	lua_setmetatable(L, -2);    // .. -> name -> new_table1
    // ..[-1][name] = new_table1, ..[-1]一般会是一个模块或者全局变量那个table。 
    lua_rawset(L, -3);          // ..
}

/*void settablename(lua_State *L, const char *label, const char *name)
{
    lua_pushstring(L, "name");
    char cname[128];
    int l1 = strlen(label);
    int l2 = strlen(name)    ;
    strncat(cname, label, 128 - l1);
    strncat(cname, name, 128 - l1 - l2);
    lua_pushstring(L, cname);
    lua_rawset(L, -3);
}*/

LUALIB_API int tolua_beginenum(lua_State *L, const char *name)
{
    // table1 是一个空表，代表这个枚举，table2是table1的原表
    //   .. 
	lua_pushstring(L, name);                        //   .. -> name                  
    lua_newtable(L);                                //   .. -> name -> table1   
    _addtoloaded(L);                                //   标记为已加载
    lua_newtable(L);                                //   .. -> name -> table1 -> table2
    lua_pushvalue(L, -1);                           //   .. -> name -> table1 -> table2 -> table2
    // 获取栈顶元素的ref, 相当于key。 会弹出栈顶元素， 返回这个索引
    int reference = luaL_ref(L, LUA_REGISTRYINDEX); //   .. -> name -> table1 -> table2         
    lua_pushlightuserdata(L, &tag);                 //   .. -> name -> table1 -> table2 -> &tag
    lua_pushnumber(L, 1);                           //   .. -> name -> table1 -> table2 -> &tag -> 1
    // table2[&tag] = 1
    lua_rawset(L, -3);                              //   .. -> name -> table1 -> table2

    lua_pushstring(L, ".name");                     //   .. -> name -> table1 -> table2 -> ".name"
                                                    // local fullName = namespace + name
    _pushfullname(L, -4);                           //   .. -> name -> table1 -> table2 -> ".name" -> fullName
    // table2[".name"] = fullName
    lua_rawset(L, -3);                              //   .. -> name -> table1 -> table2

    // 添加 __index 方法
	lua_pushstring(L, "__index");
	lua_pushcfunction(L, enum_index_event);
	lua_rawset(L, -3);

    // 添加 __newindex 方法
	lua_pushstring(L, "__newindex");
	lua_pushcfunction(L, enum_newindex_event);
	lua_rawset(L, -3);	

	return reference;
}

LUALIB_API void tolua_endenum(lua_State *L)
{
    //   .. -> name -> table1 -> table2
    // setmetatable(table1, table2)
	lua_setmetatable(L, -2);    // .. -> name -> table1
    // ..[-1][name] = table1
    lua_rawset(L, -3);          // ..
}

LUALIB_API void tolua_beginstaticclass(lua_State *L, const char *name)
{    
    lua_pushstring(L, name);  
    lua_newtable(L);
    _addtoloaded(L);
    lua_pushvalue(L, -1);

    lua_pushlightuserdata(L, &tag);
    lua_pushnumber(L, 1);
    lua_rawset(L, -3);

    lua_pushstring(L, ".name");
    _pushfullname(L, -4);
    lua_rawset(L, -3);

    lua_pushstring(L, "__index");
    lua_pushcfunction(L, static_index_event);
    lua_rawset(L, -3);

    lua_pushstring(L, "__newindex");
    lua_pushcfunction(L, static_newindex_event);
    lua_rawset(L, -3);      
}

LUALIB_API void tolua_endstaticclass(lua_State *L)
{
    lua_setmetatable(L, -2);
    lua_rawset(L, -3);    
}

LUALIB_API void tolua_constant(lua_State *L, const char *name, double value)
{
    lua_pushstring(L, name);
    lua_pushnumber(L, value);
    lua_rawset(L,-3);
}

LUALIB_API void tolua_function(lua_State *L, const char *name, lua_CFunction fn)
{

    // 进入前栈中元素
    // .. -> name -> new_table1 -> rereference_table

  	lua_pushstring(L, name);    // .. -> name -> new_table1 -> rereference_table -> name
    tolua_pushcfunction(L, fn); // .. -> name -> new_table1 -> rereference_table -> name -> fn

    // rereference_table[name] = fn
  	lua_rawset(L, -3);          // .. -> name -> new_table1 -> rereference_table

    /*lua_pushstring(L, name);
    lua_pushcfunction(L, fn);
    lua_rawset(L, -3);*/
}

// 注册C#一个属性的get和set方法
// rereference_table[&gettag] = {nameA = funcA, nameB = funcB, ...}
// rereference_table[&settag] = {nameA = funcA, nameB = funcB, ...}
// 所有属性都存在放在一个table里面的，属性名字是key, 具体实现函数是value
LUALIB_API void tolua_variable(lua_State *L, const char *name, lua_CFunction get, lua_CFunction set)
{                

    // new_table1 当前实际创建的类，就是一个空table， 啥都系都注册到metatable里面的 (rereference_table)

    // .. -> name -> new_table1 -> rereference_table

    lua_pushlightuserdata(L, &gettag);  // .. -> name -> new_table1 -> rereference_table -> &gettag
    lua_rawget(L, -2);                  // .. -> name -> new_table1 -> rereference_table -> rereference_table[&gettag]

    if (!lua_istable(L, -1)) // 不是table的话，大概率是空
    {
        /* create .get table, leaving it at the top */
        lua_pop(L, 1);                      // .. -> name -> new_table1 -> rereference_table
        lua_newtable(L);                    // .. -> name -> new_table1 -> rereference_table -> {new_table2}
        lua_pushlightuserdata(L, &gettag);  // .. -> name -> new_table1 -> rereference_table -> {new_table2} -> &gettag
        lua_pushvalue(L, -2);               // .. -> name -> new_table1 -> rereference_table -> {new_table2} -> &gettag -> {new_table2}
        // rereference_table[&gettag] = {new_table2}
        lua_rawset(L, -4);                  // .. -> name -> new_table1 -> rereference_table -> {new_table2}
    }

    // .. -> name -> new_table1 -> rereference_table -> rereference_table[&gettag] or {newtable2}
    
    lua_pushstring(L, name);            // .. -> name -> new_table1 -> rereference_table -> {new_table2} -> name
    //lua_pushcfunction(L, get);
    tolua_pushcfunction(L, get);        // .. -> name -> new_table1 -> rereference_table -> {new_table2} -> name -> get
    // {new_table2}[name] = get
    lua_rawset(L, -3);                  /* store variable */ // .. -> name -> new_table1 -> rereference_table -> {new_table2}
    lua_pop(L, 1);                      /* pop .get table */ // .. -> name -> new_table1 -> rereference_table

    // 同上
    /* set func */
    if (set != NULL)
    {        
        lua_pushlightuserdata(L, &settag);
        lua_rawget(L, -2);

        if (!lua_istable(L, -1))
        {
            /* create .set table, leaving it at the top */
            lua_pop(L, 1);
            lua_newtable(L);            
            lua_pushlightuserdata(L, &settag);
            lua_pushvalue(L, -2);
            lua_rawset(L, -4);
        }

        lua_pushstring(L, name);
        //lua_pushcfunction(L, set);
        tolua_pushcfunction(L, set);
        lua_rawset(L, -3);                  /* store variable */
        lua_pop(L, 1);                      /* pop .set table */
    }
}

// C#端获取lua端对象时，需要使用这个函数，获取到lua对象的一个引用(Int)。 lua端需要缓存起来。保证不被回收
LUALIB_API int toluaL_ref(lua_State *L)
{
    // #define abs_index(L, i)  ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)

    // LUA_RIDX_FIXEDMAP 存放C#端对lua端对象的引用。key : C# userdata,  value : lua obj reference（唯一索引，地址）

    // .. -> lua obj -> 

	int stackPos = abs_index(L, -1);	// 获取栈顶位置
	lua_getref(L, LUA_RIDX_FIXEDMAP);   // lua obj -> LUA_RIDX_FIXEDMAP table
	lua_pushvalue(L, stackPos);         // lua obj -> LUA_RIDX_FIXEDMAP table -> lua obj
	lua_rawget(L, -2);                  // lua obj -> LUA_RIDX_FIXEDMAP table -> 获取到的值

    // 如果获取到了 reference
	if (!lua_isnil(L, -1))                          // lua obj -> LUA_RIDX_FIXEDMAP table -> lua obj reference 
	{
		int ref = (int)lua_tointeger(L, -1);        // lua obj -> LUA_RIDX_FIXEDMAP table -> lua obj reference
		lua_pop(L, 3);                              // .. ->
		return ref;
	}
	else                                // 如果没有获取到
	{
		lua_pushvalue(L, stackPos);                 // lua obj -> LUA_RIDX_FIXEDMAP table -> nil -> lua obj
		int ref = luaL_ref(L, LUA_REGISTRYINDEX);   // lua obj -> LUA_RIDX_FIXEDMAP table -> nil
		lua_pushvalue(L, stackPos);                 // lua obj -> LUA_RIDX_FIXEDMAP table -> nil -> lua obj
		lua_pushinteger(L, ref);                    // lua obj -> LUA_RIDX_FIXEDMAP table -> nil -> lua obj -> lua obj reference
		lua_rawset(L, -4);                          // lua obj -> LUA_RIDX_FIXEDMAP table -> nil
		lua_pop(L, 3);                              // .. ->
		return ref;
	}
}

LUALIB_API void toluaL_unref(lua_State *L, int reference)
{
	lua_getref(L, LUA_RIDX_FIXEDMAP);
	lua_getref(L, reference);
	lua_pushnil(L);
	lua_rawset(L, -3);
	luaL_unref(L, LUA_REGISTRYINDEX, reference);
	lua_pop(L, 1);
}

LUA_API lua_State* tolua_getmainstate(lua_State *L1)
{
	lua_rawgeti(L1, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
	lua_State *L = lua_tothread(L1, -1);
	lua_pop(L1, 1);
	return L;
}

// 返回指定索引处元素的类型，一个整数，整数的值在System.ValueType.lua中定义
// 可以调用AddValueType添加类型
LUA_API int tolua_getvaluetype(lua_State *L, int stackPos)
{
 
    // .. ->

    // #define abs_index(L, i)  ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
	stackPos = abs_index(L, stackPos);      // 支持负数而已, 把负数变成整数

    // 获取 全局函数 GetLuaValueType， 压入栈
	lua_getref(L, LUA_RIDX_CHECKVALUE); // .. -> GetLuaValueType
	lua_pushvalue(L, stackPos);         // .. -> GetLuaValueType -> stack[stackPos]
    // GetLuaValueType(stack[stackPos])
	lua_call(L, 1, 1);                  // .. -> result
    // 将返回值变成int类型
	int ret = (int)lua_tonumber(L, -1);
    // 弹出结果 
	lua_pop(L, 1);                      // .. ->
	return ret;
}

LUALIB_API bool tolua_createtable(lua_State *L, const char *path, int szhint)
{
	const char *e = NULL;
	lua_pushvalue(L, LUA_GLOBALSINDEX);						//stack _G

	do 
	{
	  e = strchr(path, '.');
	  if (e == NULL) e = path + strlen(path);
	  lua_pushlstring(L, path, e - path);					//stack _G key
	  lua_rawget(L, -2);									//stack _G value
	  int type = lua_type(L, -1);

	  if (type == LUA_TNIL) 
	  {  
	    lua_pop(L, 1); 										//stack _G
	    lua_createtable(L, 0, (*e == '.' ? 1 : szhint));	//stack _G table
	    lua_pushlstring(L, path, e - path);					//stack _G table name
	    lua_pushvalue(L, -2);								//stack _G table name table
	    lua_settable(L, -4);  								//stack _G table
	  }
	  else if (type != LUA_TTABLE) 
	  {  
	    lua_pop(L, 2);  
	    return false;  
	  }

	  lua_remove(L, -2);  									//stack table
	  path = e + 1;
	} while (*e == '.');

	return true;
}

LUALIB_API bool tolua_beginpremodule(lua_State *L, const char *path, int szhint)
{
    const char *e = NULL;
    const char *name = path;
    lua_pushvalue(L, LUA_GLOBALSINDEX);                     //stack _G

    do 
    {
        e = strchr(path, '.');
        if (e == NULL) e = path + strlen(path);
        lua_pushlstring(L, path, e - path);                   //stack t key
        lua_rawget(L, -2);                                    //stack t value
        int type = lua_type(L, -1);

        if (type == LUA_TNIL) 
        {
            lua_pop(L, 1);                                      //stack t
            lua_createtable(L, 0, (*e == '.' ? 1 : szhint));    //stack t table
            lua_pushlstring(L, path, e - path);                 //stack t table name
            lua_pushvalue(L, -2);                               //stack t table name table
            lua_settable(L, -4);                                //stack t table

            lua_pushstring(L, ".name");        
            pushmodule(L, name);
            lua_rawset(L, -3);               

            lua_pushstring(L, "__index");
            lua_pushcfunction(L, module_index_event);
            lua_rawset(L, -3);    
        }
        else if (type != LUA_TTABLE) 
        {  
            lua_pop(L, 1);  
            return false;  
        }

        lua_remove(L, -2);                                    //stack table
        path = e + 1;
    } while (*e == '.');

    lua_pushstring(L, ".name");
    lua_gettable(L, -2);      
    sb.buffer = lua_tolstring(L, -1, &sb.len);    
    lua_pop(L, 1);
    return true;
}

LUALIB_API bool tolua_endpremodule(lua_State *L, int ref)
{    
    lua_getref(L, ref);
    lua_pushstring(L, ".name");
    lua_rawget(L, -2);                

    if (!tolua_createtable(L, lua_tostring(L, -1), 0))
    {
        lua_pushnil(L);
    }
    
    sb.len = 0;
    return true;
}

LUALIB_API bool tolua_addpreload(lua_State *L, const char *path)
{    
    const char *e = NULL;
    const char *name = path;
    int top = lua_gettop(L);
    lua_pushvalue(L, LUA_GLOBALSINDEX);                         //stack _G

    do 
    {
        e = strchr(path, '.');
        if (e == NULL) e = path + strlen(path);
        lua_pushlstring(L, path, e - path);                     //stack t key
        lua_rawget(L, -2);                                      //stack t value
        int type = lua_type(L, -1);

        if (type == LUA_TNIL) 
        {
            lua_pop(L, 1);                                      //stack t
            lua_createtable(L, 0, 0);                           //stack t table
            lua_pushlstring(L, path, e - path);                 //stack t table name
            lua_pushvalue(L, -2);                               //stack t table name table
            lua_settable(L, -4);                                //stack t table

            lua_pushstring(L, ".name");        
            lua_pushstring(L, name);
            lua_rawset(L, -3);               

            lua_pushstring(L, "__index");
            lua_pushcfunction(L, module_index_event);
            lua_rawset(L, -3);    
        }
        else if (type != LUA_TTABLE) 
        {  
            lua_settop(L, top);
            return false;  
        }

        lua_remove(L, -2);                                      //stack table
        path = e + 1;
    } while (*e == '.');

    lua_settop(L, top);
    return true;
}

LUALIB_API int tolua_getclassref(lua_State *L, int pos)
{    
    lua_getmetatable(L, pos);           //mt
    lua_pushstring(L, ".ref");          //mt .ref
    lua_rawget(L, -2);                  //mt ref
    int ref = (int)lua_tointeger(L, -1);         
    return ref;
}

LUALIB_API bool tolua_pushluatable(lua_State *L, const char *path)
{
	const char *e = NULL;
	lua_pushvalue(L, LUA_GLOBALSINDEX);	

	do 
	{
	  e = strchr(path, '.');
	  if (e == NULL) e = path + strlen(path);
	  lua_pushlstring(L, path, e - path);
	  lua_rawget(L, -2);

	  if (!lua_istable(L, -1))
	  {  
	    lua_pop(L, 2); 
	    return false;
	  }

	  lua_remove(L, -2);  
	  path = e + 1;
	} while (*e == '.');

	return true;
}

LUALIB_API const char* tolua_typename(lua_State *L, int lo)
{
    int tag = lua_type(L,lo);
    
    if (tag == LUA_TNONE)
    {
		lua_pushstring(L,"[no object]");
    }
    else if (tag != LUA_TUSERDATA && tag != LUA_TTABLE)
    {
        lua_pushstring(L, lua_typename(L,tag));
    }
    else if (tag == LUA_TUSERDATA)
    {
        if (!lua_getmetatable(L,lo))
        {
            lua_pushstring(L, lua_typename(L,tag));
        }
        else
        {
            lua_pushstring(L, ".name");
            lua_rawget(L, -2);

            if (!lua_isstring(L,-1))
            {
                lua_pop(L,1);
                lua_pushstring(L,"[undefined]");
            }
        }
    }
    else  //is table
    {
        lua_pushvalue(L,lo);        

        if (!lua_getmetatable(L,lo))
        {
            lua_pop(L,1);
            lua_pushstring(L,"table");
        }
        else
        {
            lua_pushstring(L, ".name");
            lua_rawget(L, -2);

            lua_pushstring(L,"class ");
            lua_insert(L,-2);
            lua_concat(L,2);
        }
    }

    return lua_tostring(L, -1);
}

LUALIB_API int tolua_getmetatableref(lua_State *L, int pos)
{
	int ref = LUA_REFNIL;	

    if (lua_getmetatable(L, pos) != 0)
    {
        lua_pushstring(L, ".ref");
        lua_rawget(L, -2);	

        if (lua_isnumber(L, -1))
        {
            ref = (int)lua_tointeger(L, -1);
        }

        lua_pop(L, 2);
    }

    return ref;
}

static lua_State* getthread(lua_State *L, int *arg) 
{
    if (lua_isthread(L, 1)) 
    {
        *arg = 1;
        return lua_tothread(L, 1);
    }
    else 
    {
        *arg = 0;
        return L;  
    }
}

static int traceback(lua_State *L) 
{
    int arg;
    lua_State *L1 = getthread(L, &arg);
    const char *msg = lua_tostring(L, arg + 1);

    if (msg == NULL && !lua_isnoneornil(L, arg + 1))  
    {
        lua_pushvalue(L, arg + 1);  
    }
    else 
    {
        if (NULL != strstr(msg, "stack traceback:"))
        {
            lua_pushvalue(L, arg + 1);
            return 1;
        }

        int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0);
#ifdef LUAJIT_VERSION            
        luaL_traceback(L, L1, msg, level);
#else        
        lua_getref(L, LUA_RIDX_TRACEBACK);
        lua_pushthread(L1);
        lua_pushvalue(L, arg + 1);
        lua_pushnumber(L, level + 1);
        lua_call(L, 3, 1);   
#endif            
    }     

    return 1;
}

LUALIB_API int tolua_beginpcall(lua_State *L, int reference)
{	
    lua_getref(L, LUA_RIDX_CUSTOMTRACEBACK);
	int top = lua_gettop(L);
	lua_getref(L, reference);
	return top;
}

LUALIB_API void tolua_pushtraceback(lua_State *L)
{
	lua_getref(L, LUA_RIDX_CUSTOMTRACEBACK);
}

/*static const int sentinel_ = 0;
#define sentinel ((void *)&sentinel_)

static int _require(lua_State *L)
{
    const char *name = luaL_checkstring(L, 1);    
    lua_settop(L, 1);  
    const char *key = luaL_gsub(L, name, "/", ".");            
    lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
    lua_getfield(L, 3, key);

    if (lua_toboolean(L, -1)) 
    {  
        if (lua_touserdata(L, -1) == sentinel)  
        {
            luaL_error(L, "loop or previous error loading module " LUA_QS, name);
        }

        return 1;  //package is already loaded
    }

    // else must load it; iterate over available loaders
    lua_getfield(L, LUA_ENVIRONINDEX, "loaders");

    if (!lua_istable(L, -1))
    {
        luaL_error(L, LUA_QL("package.loaders") " must be a table");
    }

    lua_pushliteral(L, "");  //error message accumulator

    for(int i = 1; ; i++) 
    {
        lua_rawgeti(L, -2, i);  // get a loader

        if (lua_isnil(L, -1))
        {
            luaL_error(L, "module " LUA_QS " not found:%s", name, lua_tostring(L, -2));
        }

        lua_pushstring(L, name);
        lua_call(L, 1, 1);  

        if (lua_isfunction(L, -1))  // did it find module?
        {
            break;  // module loaded successfully
        }
        else if (lua_isstring(L, -1))  // loader returned error message?
        {
            lua_concat(L, 2);  // accumulate it
        }
        else
        {
            lua_pop(L, 1);            
        }    
    }

    lua_pushlightuserdata(L, sentinel);
    lua_setfield(L, 3, key);   // _LOADED[name] = sentinel
    lua_pushstring(L, name);    // pass name as argument to module
    lua_call(L, 1, 1);          // run loaded module

    if (!lua_isnil(L, -1))  // non-nil return? 
    {
        lua_setfield(L, 3, key);  // _LOADED[name] = returned value
    }

    lua_getfield(L, 3, key);

    if (lua_touserdata(L, -1) == sentinel) 
    {   // module did not set a value? 
        lua_pushboolean(L, 1);          // use true as result
        lua_pushvalue(L, -1);           // extra copy to be returned
        lua_setfield(L, 3, key);        // _LOADED[name] = true 
    }

    return 1;
}

void tolua_openrequire(lua_State *L)
{    
    lua_getglobal(L, "require");
    lua_pushcfunction(L, _require);
    lua_getfenv(L, -2);
    lua_setfenv(L, -2);
    lua_setglobal(L, "require");
    lua_pop(L, 1);
}*/

LUALIB_API int tolua_require(lua_State *L, const char *fileName)
{
    int top = lua_gettop(L);
    lua_getref(L, LUA_RIDX_CUSTOMTRACEBACK);  // .. -> registry[LUA_RIDX_CUSTOMTRACEBACK]
    lua_getref(L, LUA_RIDX_REQUIRE);    // .. -> registry[LUA_RIDX_CUSTOMTRACEBACK] -> registry[LUA_RIDX_REQUIRE]
    lua_pushstring(L, fileName);        // .. -> registry[LUA_RIDX_CUSTOMTRACEBACK] -> registry[LUA_RIDX_REQUIRE] -> filname
    int ret = lua_pcall(L, 1, -1, top + 1);
    lua_remove(L, top + 1);    
    return ret;
}


/*LUALIB_API bool tolua_checkluaslot(lua_State *L, int stackPos, int *func, int *table)
{
    lua_pushstring(L, "__call");      
    lua_rawget(L, stackPos);

    if (lua_isfunction(L, -1))
    {
        *func = toluaL_ref(L);        
    }
    else
    {
        lua_pop(L, 1);
        return false;
    }

    lua_pushvalue(L, stackPos);
    *table = toluaL_ref(L);    
    return true;
}*/

/*static int do_operator (lua_State *L, const char *op)
{
    if (lua_isuserdata(L,1))
    {        
        lua_pushvalue(L,1);  

        while (lua_getmetatable(L,-1))
        {   
            lua_remove(L,-2);                      
            lua_pushstring(L,op);               
            lua_rawget(L,-2);    

            if (lua_isfunction(L,-1))
            {
                lua_pushvalue(L,1);
                lua_pushvalue(L,2);
                lua_call(L,2,1);
                return 1;
            }

            lua_settop(L,3);
        }
    }

    luaL_error(L,"attempt to perform operation %s failed", op);
    return 0;
}

static int class_add_event (lua_State *L)
{
    return do_operator(L,"op_Addition");
}

static int class_sub_event (lua_State *L)
{
    return do_operator(L,"op_Subtraction");
}

static int class_mul_event (lua_State *L)
{
    return do_operator(L,"op_Multiply");
}

static int class_div_event (lua_State *L)
{
    return do_operator(L,"op_Division");
}

static int class_equals_event (lua_State *L)
{
    return do_operator(L,"op_Equality");
}*/


#ifdef _WIN32
double tolua_timegettime()
{
	FILETIME ft;
	double t;
	GetSystemTimeAsFileTime(&ft);
	/* Windows file time (time since January 1, 1601 (UTC)) */
	t = ft.dwLowDateTime / 1.0e7 + ft.dwHighDateTime*(4294967296.0 / 1.0e7);
	/* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */
	return (t - 11644473600.0);
}
#else
double tolua_timegettime()
{
	struct timeval v;
	gettimeofday(&v, (struct timezone *) NULL);
	/* Unix Epoch time (time since January 1, 1970 (UTC)) */
	return v.tv_sec + v.tv_usec / 1.0e6;
}
#endif

static int tolua_gettime(lua_State *L)
{
	lua_pushnumber(L, tolua_timegettime());
	return 1;
}

// peer 扩展 C#那边的对象， 增加属性，方法啥的
// tolua.setpeer(c++_obj,lua_table)
static int tolua_bnd_setpeer(lua_State *L) 
{
    // stack: userdata, table
    // userdata -> peer table
    if (!lua_isuserdata(L, -2)) 
    {
        return luaL_error(L, "Invalid argument #1 to setpeer: userdata expected.");        
    }

    // 如果传空，设置为全局表
    if (lua_isnil(L, 2)) 
    {
        lua_pop(L, 1);                                      // userdata
        lua_pushvalue(L, TOLUA_NOPEER);                     // userdata -> _G
        // 设置userdata的环境为_G
        lua_setfenv(L, -2);                                 // userdata            
    }        
    else
    {
        // new table的作用是为了方便访问扩展前和扩展后的内容。扩展前的内容再userdata上。扩展后的内容再peer表上。
        // 要访问扩展前的内容，通过peer.base or userdata.base 就能拿到这个vptr table.然后通过元方法。拿到userdata的数据。
        // 直接通过userdata.xx。 会优先查找peer表的。所以peer表也可以叫继承。peer是子类。userdata是基类
        // setfenv(userdata, peer)
        // local newtable = {}
        // newtable[&vptr] = userdata
        // loval vptrTab = _G[LUA_RIDX_VPTR]
        // setmetable(newtable, vptrTab)
        // peer["base"] = newtable
        // 设置peer表的base, C++_obj 也能通过C++_obj.base访问到。因为是peer表

        lua_pushvalue(L, 2);                //stack: u p p      // userdata -> peer table ->  peer table
        // 设置userdata的环境为peer table
        lua_setfenv(L, -3);                 //stack: u p        // userdata -> peer table
        lua_newtable(L);                    //stack: u p vt     // userdata -> peer table -> newtable
        
        // &vptr 一个固定值，当作key, 
        lua_pushlightuserdata(L, &vptr);                        // userdata -> peer table -> newtable -> &vptr
        lua_pushvalue(L, 1);                                    // userdata -> peer table -> newtable -> &vptr -> userdata
        // newtable[&vptr] = userdata
        lua_rawset(L, -3);                                      // userdata -> peer table -> newtable
        //lua_pushvalue(L, 1);
        //lua_rawseti(L, -2, 1);

        // 全局中获取 LUA_RIDX_VPTR 这个table
        lua_getref(L, LUA_RIDX_VPTR);       //stack: u p vt mt  // userdata -> peer table -> newtable -> LUA_RIDX_VPTR table
        // setmetable(newtable, LUA_RIDX_VPTR table)
        lua_setmetatable(L, -2);            //stack: u p vt     // userdata -> peer table -> newtable

        lua_pushstring(L, "base");          //stack: u p vt "base"       // userdata -> peer table -> newtable -> "base"
        lua_pushvalue(L, -2);               //stack: u p vt "base" vt    // userdata -> peer table -> newtable -> "base" -> newtable
        // peer table["base"] = newtable
        lua_rawset(L, 2);                   //stack: u p vt              // userdata -> peer table -> newtable
        lua_pop(L, 1);
    }

    return 0;
};


// 获取一个peer表， 没有peer表返回空
static int tolua_bnd_getpeer(lua_State *L) 
{    
    lua_getfenv(L, -1);

    if (lua_rawequal(L, -1, TOLUA_NOPEER)) 
    {
        lua_pop(L, 1);
        lua_pushnil(L);
    };

    return 1;
};

static int tolua_bnd_getfunction(lua_State *L)
{
    lua_pushvalue(L, 1);                            // stack: obj key obj

    while (lua_getmetatable(L, -1) != 0)            // stack: obj key mt
    {            
        lua_remove(L, -2);                          // stack: obj key mt

        lua_pushvalue(L, 2);                        // stack: obj key mt key
        lua_rawget(L, -2);                          // stack: obj key mt value        

        if (!lua_isfunction(L, -1))
        {
            return 1;
        }
        else
        {
            lua_pop(L, 1);
        }
        
        lua_pushlightuserdata(L, &gettag);          
        lua_rawget(L, -2);                          //stack: obj key mt tget

        if (lua_istable(L, -1))
        {
            lua_pushvalue(L, 2);                    //stack: obj key mt tget key
            lua_rawget(L, -2);                      //stack: obj key mt tget value 

            if (lua_isfunction(L, -1))
            {                                           
                return 1;
            }
        }
            
        lua_settop(L, 3);                           //stack: obj key mt
    }

    lua_settop(L, 2);
    lua_pushnil(L);
    return 1;
}

static int tolua_bnd_type (lua_State *L)
{
    tolua_typename(L,lua_gettop(L));
    return 1;
}

// peer 表绑定 gettable
// local gettable = tolua.initget(peertable)
// 返回 gettable
static int tolua_initgettable(lua_State *L)
{
    // peertable
    if (!lua_istable(L, 1))
    {        
        return luaL_typerror(L, 1, "table");         
    }

    lua_newtable(L);                    // peertable -> new table
    lua_pushlightuserdata(L, &gettag);  // peertable -> new table -> &gettag
    lua_pushvalue(L, -2);               // peertable -> new table -> &gettag -> new table
    // peertable[&gettag] = new table
    lua_rawset(L, 1);                   // peertable -> new table
    return 1;                           // 返回1， 表示这个函数有一个返回值，lua层会拿到 newtable, 也就是gettable
}

// peer 表绑定 settable
// local settable = tolua.initset(peertable)
// 返回 settable
static int tolua_initsettable(lua_State *L)
{
    // peertable
    if (!lua_istable(L, 1))
    {        
        return luaL_typerror(L, 1, "table");         
    }

    lua_newtable(L);                    // peertable -> new table
    lua_pushlightuserdata(L, &settag);  // peertable -> new table -> &settag
    lua_pushvalue(L, -2);               // peertable -> new table -> &settag -> new table
    // peertable[&settag] = newtable
    lua_rawset(L, 1);                   // peertable -> new table
    return 1;                           // 返回1， 表示这个函数有一个返回值，lua层会拿到 newtable, 也就是settable
}

// vptr，也就是C++的virtual table。
// 用来实现在lua层使用table继承C#的userdata机制。
// _G[LUA_RIDX_VPTR] = {}. 增加__index 和 __newindex 方法
void tolua_openvptr(lua_State *L)
{
    lua_newtable(L);        

    lua_pushstring(L, "__index");
    lua_pushcfunction(L, vptr_index_event);
    lua_rawset(L, -3);  

    lua_pushstring(L, "__newindex");
    lua_pushcfunction(L, vptr_newindex_event);
    lua_rawset(L, -3);  

    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_VPTR); 
}

static const struct luaL_Reg tolua_funcs[] = 
{
	{ "gettime", tolua_gettime },
	{ "typename", tolua_bnd_type },
	{ "setpeer", tolua_bnd_setpeer},
	{ "getpeer", tolua_bnd_getpeer},
    { "getfunction", tolua_bnd_getfunction},
    { "initset", tolua_initsettable},
    { "initget", tolua_initgettable},
    { "int64", tolua_newint64},        
    { "uint64", tolua_newuint64},
    { "traceback", traceback},
	{ NULL, NULL }
};

void tolua_setluabaseridx(lua_State *L)
{    
	for (int i = 1; i <= 64; i++)
	{
		lua_pushinteger(L, i);
		lua_rawseti(L, LUA_REGISTRYINDEX, i);
	}

    //同lua5.1.5之后版本放入mainstate和_G
	lua_pushthread(L);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);

	lua_pushvalue(L, LUA_GLOBALSINDEX);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);

    //cache require函数
    lua_getglobal(L, "require");
    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_REQUIRE);      
}

void tolua_openpreload(lua_State *L)
{
    lua_getglobal(L, "package");
    lua_pushstring(L, "preload");
    lua_rawget(L, -2);
    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_PRELOAD);    
    lua_pushstring(L, "loaded");
    lua_rawget(L, -2);
    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_LOADED);    
    lua_pop(L, 1);
}

void tolua_opentraceback(lua_State *L)
{
    lua_getglobal(L, "debug");
    lua_pushstring(L, "traceback");
    lua_rawget(L, -2);
    lua_pushvalue(L, -1);
    lua_setfield(L, LUA_GLOBALSINDEX, "traceback");            
    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_TRACEBACK);
    lua_pop(L, 1);    

    lua_pushcfunction(L, traceback);
    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_CUSTOMTRACEBACK);    
}

// 存放lua引用C#的对象。value为弱引用
void tolua_openubox(lua_State *L)
{
	lua_newtable(L);
	lua_newtable(L);            
	lua_pushstring(L, "__mode");
	lua_pushstring(L, "v");
	lua_rawset(L, -3);
	lua_setmetatable(L, -2);            
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_UBOX);	
}


// key为C#引用的lua对象（如function，table），value是number。
// 存放被C# 引用的对象，防止被垃圾回收
void tolua_openfixedmap(lua_State *L)
{
	lua_newtable(L);
	//lua_newtable(L);
	//lua_pushstring(L, "__mode");
	//lua_pushstring(L, "k");
	//lua_rawset(L, -3);
	//lua_setmetatable(L, -2);    	
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_FIXEDMAP);		
}

//对于下列读取lua 特定文件需要判空报错
void tolua_openvaluetype(lua_State *L)
{
    // GetLuaValueType 定义在lua文件中  System.ValueType.lua
    // 获取全局变量 GetLuaValueType
	lua_getglobal(L, "GetLuaValueType");
    // 放入全局table中  LUA_REGISTRYINDEX[LUA_RIDX_CHECKVALUE] = GetLuaValueType
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_CHECKVALUE);	
}

void tolua_openluavec3(lua_State *L)
{    
	lua_getglobal(L, "Vector3");

    if (!lua_istable(L, 1))
    {        
        luaL_error(L, "Vector3 does not exist or not be loaded");
        return;
    }

    // C# Vector3 转化为lua Vector3对象，调用Vector3.New(x, y, z)
	lua_pushstring(L, "New");
	lua_rawget(L, -2);
    // LUA_REGISTRYINDEX[LUA_RIDX_PACKVEC3] = Vector3.New
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_PACKVEC3);	
	lua_pushstring(L, "Get");
	lua_rawget(L, -2);
    // LUA_REGISTRYINDEX[LUA_RIDX_UNPACKVEC3] = Vector3.Get
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_UNPACKVEC3);	
	lua_pop(L, 1);
}

void tolua_openluavec2(lua_State *L)
{    
	lua_getglobal(L, "Vector2");

    if (!lua_istable(L, 1))
    {        
        luaL_error(L, "Vector2 does not exist or not be loaded");
        return;
    }

	lua_pushstring(L, "New");
	lua_rawget(L, -2);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_PACKVEC2);	
	lua_pushstring(L, "Get");
	lua_rawget(L, -2);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_UNPACKVEC2);	
	lua_pop(L, 1);
}

void tolua_openluavec4(lua_State *L)
{    
	lua_getglobal(L, "Vector4");

    if (!lua_istable(L, 1))
    {        
        luaL_error(L, "Vector4 does not exist or not be loaded");
        return;
    }

	lua_pushstring(L, "New");
	lua_rawget(L, -2);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_PACKVEC4);	
	lua_pushstring(L, "Get");
	lua_rawget(L, -2);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_UNPACKVEC4);	
	lua_pop(L, 1);
}

void tolua_openluaclr(lua_State *L)
{    
	lua_getglobal(L, "Color");

    if (!lua_istable(L, 1))
    {        
        luaL_error(L, "Color does not exist or not be loaded");
        return;
    }

	lua_pushstring(L, "New");
	lua_rawget(L, -2);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_PACKCLR);	
	lua_pushstring(L, "Get");
	lua_rawget(L, -2);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_UNPACKCLR);	
	lua_pop(L, 1);
}

void tolua_openluaquat(lua_State *L)
{    
	lua_getglobal(L, "Quaternion");

    if (!lua_istable(L, 1))
    {        
        luaL_error(L, "Quaternion does not exist or not be loaded");
        return;
    }

	lua_pushstring(L, "New");
	lua_rawget(L, -2);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_PACKQUAT);	
	lua_pushstring(L, "Get");
	lua_rawget(L, -2);
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_UNPACKQUAT);		
	lua_pop(L, 1);
}

void tolua_openlualayermask(lua_State *L)
{    
    lua_getglobal(L, "LayerMask");   

    if (!lua_istable(L, 1))
    {        
        luaL_error(L, "LayerMask does not exist or not be loaded");
        return;
    }

    lua_pushstring(L, "New");
    lua_rawget(L, -2);
    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_PACKLAYERMASK);   
    lua_pushstring(L, "Get");
    lua_rawget(L, -2);
    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_UNPACKLAYERMASK); 
    lua_pop(L, 1);
}

void tolua_openupdate(lua_State *L)
{
    lua_getglobal(L, "Update");

    if (!lua_isfunction(L, 1))
    {
        luaL_error(L, "Update function does not exist or not be loaded");
        return;
    }

    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_UPDATE);    
    lua_getglobal(L, "LateUpdate");

    if (!lua_isfunction(L, 1))
    {
        luaL_error(L, "LateUpdate function does not exist or not be loaded");
        return;
    }

    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_LATEUPDATE);  
    lua_getglobal(L, "FixedUpdate");

    if (!lua_isfunction(L, 1))
    {
        luaL_error(L, "FixedUpdate function does not exist or not be loaded");
        return;
    }

    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_FIXEDUPDATE);      
}


static int _openlualibs(lua_State *L)
{
    tolua_openvaluetype(L);
    tolua_openluavec3(L);
    tolua_openluavec2(L);
    tolua_openluavec4(L);
    tolua_openluaclr(L);
    tolua_openluaquat(L);   
    tolua_openlualayermask(L);
    tolua_openupdate(L);
    return 0;
}

LUALIB_API int tolua_openlualibs(lua_State *L)
{    
    lua_pushcfunction(L, _openlualibs);
    return lua_pcall(L, 0, -1, 0);    
}

static int mathf_ispoweroftwo(lua_State *L)
{
    lua_Integer mask = luaL_checkinteger(L, 1);
    bool flag = (mask & (mask-1)) == 0 ? true : false;
    lua_pushboolean(L, flag);
    return 1;
}

lua_Integer NextPowerOfTwo(lua_Integer v)
{
    v -= 1;
    v |= v >> 16;
    v |= v >> 8;
    v |= v >> 4;
    v |= v >> 2;
    v |= v >> 1;
    return v + 1;
}

static int mathf_nextpoweroftwo(lua_State *L)
{
    lua_Integer v = luaL_checkinteger(L, 1);
    v = NextPowerOfTwo(v);
    lua_pushnumber(L, v);
    return 1;
}

static int mathf_closestpoweroftwo(lua_State *L)
{
    lua_Integer v = luaL_checkinteger(L, 1);
    lua_Integer nextPower = NextPowerOfTwo (v);
    lua_Integer prevPower = nextPower >> 1;    

    if (v - prevPower < nextPower - v)
    {
        lua_pushnumber(L, prevPower);        
    }
    else
    {    
        lua_pushnumber(L, nextPower);
    }

    return 1;
}

static int mathf_gammatolinearspace(lua_State *L)
{
    lua_Number value = luaL_checknumber(L, 1);

    if (value <= 0.04045f)
    {
        value /= 12.92f;
    }
    else if (value < 1.0f)
    {    
        value = pow((value + 0.055f)/1.055f, 2.4f);
    }
    else
    {
        value = pow(value, 2.4f);
    }

    lua_pushnumber(L, value);
    return 1;    
}

static int mathf_lineartogammaspace (lua_State *L)
{
    lua_Number value = luaL_checknumber(L, 1);

    if (value <= 0.0f)
    {    
        value = 0;
    }
    else if (value <= 0.0031308f)
    {
        value *= 12.92f;
    }
    else if (value <= 1.0f)
    {
        value = 1.055f * powf(value, 0.41666f) - 0.055f;
    }
    else
    {
        value = powf(value, 0.41666f);
    }

    lua_pushnumber(L, value);
    return 1;
}

static int mathf_normalize(lua_State *L)
{
    float x = (float)lua_tonumber(L, 1);
    float y = (float)lua_tonumber(L, 2);
    float z = (float)lua_tonumber(L, 3);

    float len = sqrt(x * x + y * y + z * z);
    
    if (len == 1)
    {
        lua_pushnumber(L, x);
        lua_pushnumber(L, y);
        lua_pushnumber(L, z);
    }
    else if (len > 1e-5)
    {
        lua_pushnumber(L, x);
        lua_pushnumber(L, y);
        lua_pushnumber(L, z);
    }
    else    
    {
        lua_pushnumber(L, 0);
        lua_pushnumber(L, 0);
        lua_pushnumber(L, 0);
    }

    return 3;
}

static const struct luaL_Reg tolua_mathf[] = 
{
    { "NextPowerOfTwo", mathf_nextpoweroftwo },
    { "ClosestPowerOfTwo", mathf_closestpoweroftwo },
    { "IsPowerOfTwo", mathf_ispoweroftwo},
    { "GammaToLinearSpace", mathf_gammatolinearspace},
    { "LinearToGammaSpace", mathf_lineartogammaspace},
    { "Normalize", mathf_normalize},
    { NULL, NULL }
};

LUALIB_API void tolua_openlibs(lua_State *L)
{   
    initmodulebuffer();
    luaL_openlibs(L);   
    int top = lua_gettop(L);    

    tolua_setluabaseridx(L);    
    tolua_opentraceback(L);
    tolua_openpreload(L);
    tolua_openubox(L);
    tolua_openfixedmap(L);    
    tolua_openint64(L);
    tolua_openuint64(L);
    tolua_openvptr(L);    
    //tolua_openrequire(L);

    luaL_register(L, "Mathf", tolua_mathf);     
    luaL_register(L, "tolua", tolua_funcs);    

    lua_getglobal(L, "tolua");

    lua_pushstring(L, "gettag");
    lua_pushlightuserdata(L, &gettag);
    lua_rawset(L, -3);

    lua_pushstring(L, "settag");
    lua_pushlightuserdata(L, &settag);
    lua_rawset(L, -3);

    lua_pushstring(L, "version");
    lua_pushstring(L, "1.0.7");
    lua_rawset(L, -3);

    lua_settop(L,top);
}

LUALIB_API void tolua_setflag(int bit, bool flag)
{
    if (flag)
    {
        toluaflags |= bit;    
    }
    else
    {
        toluaflags &= ~bit;    
    }
}

LUALIB_API bool tolua_getflag(int bit)
{
    return toluaflags & bit ? true : false;
}

static luaL_Buffer lua_buffer[3];
static int _bufferIndex = 0;

LUALIB_API luaL_Buffer* tolua_buffinit(lua_State *L)
{
    luaL_Buffer* buffer = &lua_buffer[_bufferIndex & 3];
    luaL_buffinit(L, buffer);
    ++_bufferIndex;
    return buffer;
}

LUALIB_API void tolua_addlstring(luaL_Buffer *b, const char *s, int l)
{
    luaL_addlstring(b, s, (size_t)l);
}

LUALIB_API void tolua_addstring(luaL_Buffer *b, const char *s)
{
    luaL_addstring(b, s);
}

LUALIB_API void tolua_pushresult(luaL_Buffer *b)
{
    luaL_pushresult(b);
}

LUALIB_API void tolua_addchar(luaL_Buffer *b, char c)
{
    luaL_addchar(b, c);
}

LUALIB_API int tolua_update(lua_State *L, float deltaTime, float unscaledTime)
{
    int top = tolua_beginpcall(L, LUA_RIDX_UPDATE);
    lua_pushnumber(L, deltaTime);
    lua_pushnumber(L, unscaledTime);
    return lua_pcall(L, 2, -1, top);
}

LUALIB_API int tolua_lateupdate(lua_State *L)
{
    int top = tolua_beginpcall(L, LUA_RIDX_LATEUPDATE);
    return lua_pcall(L, 0, -1, top);
}

LUALIB_API int tolua_fixedupdate(lua_State *L, float fixedTime)
{
    int top = tolua_beginpcall(L, LUA_RIDX_FIXEDUPDATE);
    lua_pushnumber(L, fixedTime);
    return lua_pcall(L, 1, -1, top);
}

static int index_op_this(lua_State *L)
{
    lua_pushvalue(L, 2);                                //stack: t, k, k
    lua_rawget(L, -2);                                  //stack: t, k, v

    if (!lua_isnil(L, -1))
    {       
        lua_pushlightuserdata(L, &vptr);               // stack: t, k, v, vptr
        lua_rawget(L, 1);                              // stack: t, k, v, u
        lua_replace(L, 1);                             // stack: u, k, v
    }    

    return 1;
}

static int newindex_op_this(lua_State *L)
{
    lua_pushvalue(L, 2);                                //stack: t, k, v, k
    lua_rawget(L, 1);                                   //stack: t, k, v, f

    if (!lua_isnil(L, -1))
    {       
        lua_pushlightuserdata(L, &vptr);               // stack: t, k, v, f, vptr
        lua_rawget(L, 1);                              // stack: t, k, v, f, u
        lua_replace(L, 1);                             // stack: u, k, v, f
    }    

    return 1;
}

LUALIB_API void tolua_regthis(lua_State *L, lua_CFunction get, lua_CFunction set)
{
    lua_newtable(L);                        //u t
    
    lua_pushlightuserdata(L, &vptr);    
    lua_pushvalue(L, -3);
    lua_rawset(L, -3);

    if (get != NULL)
    {
        lua_pushstring(L, "get");    
        tolua_pushcfunction(L, get);
        lua_rawset(L, -3);                  
    }

    if (set != NULL)
    {
        lua_pushstring(L, "set");    
        tolua_pushcfunction(L, set);
        lua_rawset(L, -3);          
    }    

    lua_pushstring(L, "__index");
    lua_pushcfunction(L, index_op_this);
    lua_rawset(L, -3);  

    lua_pushstring(L, "__newindex");
    lua_pushcfunction(L, newindex_op_this);
    lua_rawset(L, -3);          
}

LUALIB_API int tolua_where (lua_State *L, int level) 
{
    lua_Debug ar;

    if (lua_getstack(L, level, &ar)) 
    {  
        lua_getinfo(L, "Sl", &ar);  

        if (ar.currentline > 0) 
        {              
            lua_pushstring(L, ar.source);
            return ar.currentline;
        }
    }

    lua_pushliteral(L, "");
    return -1;
}